From 8e496645d8c64682afc3ab4dc6e0a4af1d5a2f4e Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 20 Sep 2018 11:01:58 +0100 Subject: [PATCH 01/47] Support redefinition of local variables with a different type --- mypy/checkmember.py | 8 +- mypy/nodes.py | 14 +- mypy/semanal.py | 55 ++++-- mypy/semanal_pass1.py | 4 + mypy/semanal_shared.py | 65 ++++++++ mypy/test/testcheck.py | 1 + test-data/unit/check-basic.test | 42 ++--- test-data/unit/check-class-namedtuple.test | 24 +-- test-data/unit/check-classes.test | 54 +++--- test-data/unit/check-columns.test | 13 +- test-data/unit/check-functions.test | 33 ++-- test-data/unit/check-generics.test | 85 +++++----- test-data/unit/check-incremental.test | 23 +-- test-data/unit/check-inference-context.test | 33 ++-- test-data/unit/check-inference.test | 176 +++++++++++--------- test-data/unit/check-isinstance.test | 11 +- test-data/unit/check-modules.test | 28 ++-- test-data/unit/check-namedtuple.test | 29 ++-- test-data/unit/check-newsyntax.test | 4 +- test-data/unit/check-optional.test | 7 +- test-data/unit/check-overloading.test | 26 +-- test-data/unit/check-redefine.test | 41 +++++ test-data/unit/check-statements.test | 5 +- test-data/unit/check-super.test | 7 +- test-data/unit/check-tuples.test | 45 ++--- test-data/unit/check-type-checks.test | 43 ++--- test-data/unit/check-typeddict.test | 7 +- test-data/unit/check-typevar-values.test | 33 ++-- test-data/unit/check-unions.test | 6 +- test-data/unit/check-unreachable-code.test | 15 +- test-data/unit/check-varargs.test | 9 +- test-data/unit/fine-grained.test | 86 ++++++---- 32 files changed, 620 insertions(+), 412 deletions(-) create mode 100644 test-data/unit/check-redefine.test diff --git a/mypy/checkmember.py b/mypy/checkmember.py index f6a6651f66f3..82af2b177173 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -9,7 +9,8 @@ ) from mypy.nodes import ( TypeInfo, FuncBase, Var, FuncDef, SymbolNode, Context, MypyFile, TypeVarExpr, - ARG_POS, ARG_STAR, ARG_STAR2, Decorator, OverloadedFuncDef, TypeAlias, TempNode + ARG_POS, ARG_STAR, ARG_STAR2, Decorator, OverloadedFuncDef, TypeAlias, TempNode, + is_final_node ) from mypy.messages import MessageBuilder from mypy.maptype import map_instance_to_supertype @@ -839,8 +840,3 @@ def erase_to_bound(t: Type) -> Type: if isinstance(t.item, TypeVarType): return TypeType.make_normalized(t.item.upper_bound) return t - - -def is_final_node(node: Optional[SymbolNode]) -> bool: - """Check whether `node` corresponds to a final attribute.""" - return isinstance(node, (Var, FuncDef, OverloadedFuncDef, Decorator)) and node.is_final diff --git a/mypy/nodes.py b/mypy/nodes.py index b744cf0dab5d..4f8d6525a8a5 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -926,15 +926,18 @@ def accept(self, visitor: StatementVisitor[T]) -> T: class AssignmentStmt(Statement): - """Assignment statement + """Assignment statement. + The same node class is used for single assignment, multiple assignment (e.g. x, y = z) and chained assignment (e.g. x = y = z), assignments - that define new names, and assignments with explicit types (# type). + that define new names, and assignments with explicit types ("# type: t" + or "x: t [= ...]"). - An lvalue can be NameExpr, TupleExpr, ListExpr, MemberExpr, IndexExpr. + An lvalue can be NameExpr, TupleExpr, ListExpr, MemberExpr, or IndexExpr. """ lvalues = None # type: List[Lvalue] + # This is a TempNode if and only if no rvalue (x: t). rvalue = None # type: Expression # Declared type in a comment, may be None. type = None # type: Optional[mypy.types.Type] @@ -2932,3 +2935,8 @@ def is_class_var(expr: NameExpr) -> bool: if isinstance(expr.node, Var): return expr.node.is_classvar return False + + +def is_final_node(node: Optional[SymbolNode]) -> bool: + """Check whether `node` corresponds to a final attribute.""" + return isinstance(node, (Var, FuncDef, OverloadedFuncDef, Decorator)) and node.is_final diff --git a/mypy/semanal.py b/mypy/semanal.py index 26cdf024f18f..3fc9aab98bf6 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -56,7 +56,7 @@ YieldExpr, ExecStmt, BackquoteExpr, ImportBase, AwaitExpr, IntExpr, FloatExpr, UnicodeExpr, TempNode, ImportedName, COVARIANT, CONTRAVARIANT, INVARIANT, UNBOUND_IMPORTED, LITERAL_YES, nongen_builtins, - get_member_expr_fullname, REVEAL_TYPE, REVEAL_LOCALS + get_member_expr_fullname, REVEAL_TYPE, REVEAL_LOCALS, is_final_node ) from mypy.literals import literal from mypy.tvar_scope import TypeVarScope @@ -82,7 +82,7 @@ from mypy import experiments from mypy.plugin import Plugin, ClassDefContext, SemanticAnalyzerPluginInterface from mypy.util import get_prefix, correct_relative_import -from mypy.semanal_shared import SemanticAnalyzerInterface, set_callable_name +from mypy.semanal_shared import SemanticAnalyzerInterface, set_callable_name, VarDefAnalyzer from mypy.scope import Scope from mypy.semanal_namedtuple import NamedTupleAnalyzer, NAMEDTUPLE_PROHIBITED_NAMES from mypy.semanal_typeddict import TypedDictAnalyzer @@ -255,6 +255,7 @@ def __init__(self, # for processing module top levels in fine-grained incremental mode. self.recurse_into_functions = True self.scope = Scope() + self.var_def_analyzer = VarDefAnalyzer() # mypyc doesn't properly handle implementing an abstractproperty # with a regular attribute so we make it a property @@ -283,6 +284,7 @@ def visit_file(self, file_node: MypyFile, fnam: str, options: Options, self.typed_dict_analyzer = TypedDictAnalyzer(options, self, self.msg) self.enum_call_analyzer = EnumCallAnalyzer(options, self) self.newtype_analyzer = NewTypeAnalyzer(options, self, self.msg) + self.var_def_analyzer.clear() with experiments.strict_optional_set(options.strict_optional): if 'builtins' in self.modules: @@ -298,8 +300,10 @@ def visit_file(self, file_node: MypyFile, fnam: str, options: Options, defs = file_node.defs self.scope.enter_file(file_node.fullname()) + self.var_def_analyzer.enter_block() for d in defs: self.accept(d) + self.var_def_analyzer.leave_block() self.scope.leave() if self.cur_mod_id == 'builtins': @@ -321,11 +325,13 @@ def refresh_partial(self, node: Union[MypyFile, FuncItem, OverloadedFuncDef], patches: List[Tuple[int, Callable[[], None]]]) -> None: """Refresh a stale target in fine-grained incremental mode.""" self.patches = patches + self.var_def_analyzer.enter_block() if isinstance(node, MypyFile): self.refresh_top_level(node) else: self.recurse_into_functions = True self.accept(node) + self.var_def_analyzer.leave_block() del self.patches def refresh_top_level(self, file_node: MypyFile) -> None: @@ -704,6 +710,7 @@ def analyze_function(self, defn: FuncItem) -> None: self.function_stack.append(defn) self.enter() for arg in defn.arguments: + self.var_def_analyzer.process_assignment(arg.variable.name(), True) self.add_local(arg.variable, defn) # The first argument of a non-static, non-class method is like 'self' @@ -715,7 +722,7 @@ def analyze_function(self, defn: FuncItem) -> None: # First analyze body of the function but ignore nested functions. self.postpone_nested_functions_stack.append(FUNCTION_FIRST_PHASE_POSTPONE_SECOND) self.postponed_functions_stack.append([]) - defn.body.accept(self) + self.visit_block(defn.body, new_var_scope=False) # Analyze nested functions (if any) as a second phase. self.postpone_nested_functions_stack[-1] = FUNCTION_SECOND_PHASE @@ -1437,6 +1444,7 @@ def allow_patching(self, parent_mod: MypyFile, child: str) -> bool: def add_module_symbol(self, id: str, as_id: str, module_public: bool, context: Context, module_hidden: bool = False) -> None: + """Add symbol that is a reference to a module object.""" if id in self.modules: m = self.modules[id] self.add_symbol(as_id, SymbolTableNode(MODULE_REF, m, @@ -1633,13 +1641,17 @@ def add_unknown_symbol(self, name: str, context: Context, is_import: bool = Fals # Statements # - def visit_block(self, b: Block) -> None: + def visit_block(self, b: Block, new_var_scope: bool = True) -> None: if b.is_unreachable: return self.block_depth[-1] += 1 + if new_var_scope: + self.var_def_analyzer.enter_block() for s in b.body: self.accept(s) self.block_depth[-1] -= 1 + if new_var_scope: + self.var_def_analyzer.leave_block() def visit_block_maybe(self, b: Optional[Block]) -> None: if b: @@ -1701,9 +1713,11 @@ def final_cb(keep_final: bool) -> None: if not keep_final: s.is_final_def = False + has_initializer = not isinstance(s.rvalue, TempNode) for lval in s.lvalues: self.analyze_lvalue(lval, explicit_type=s.type is not None, - final_cb=final_cb if s.is_final_def else None) + final_cb=final_cb if s.is_final_def else None, + has_initializer=has_initializer) self.check_final_implicit_def(s) self.check_classvar(s) s.rvalue.accept(self) @@ -1974,7 +1988,8 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> None: def analyze_lvalue(self, lval: Lvalue, nested: bool = False, add_global: bool = False, explicit_type: bool = False, - final_cb: Optional[Callable[[bool], None]] = None) -> None: + final_cb: Optional[Callable[[bool], None]] = None, + has_initializer: bool = True) -> None: """Analyze an lvalue or assignment target. Args: @@ -1984,9 +1999,10 @@ def analyze_lvalue(self, lval: Lvalue, nested: bool = False, explicit_type: Assignment has type annotation final_cb: A callback to call in situation where a final declaration on `self` overrides an existing name. + has_initializer: Is there a rvalue (i.e. not just "x: t")? """ if isinstance(lval, NameExpr): - self.analyze_name_lvalue(lval, add_global, explicit_type, final_cb) + self.analyze_name_lvalue(lval, add_global, explicit_type, final_cb, has_initializer) elif isinstance(lval, MemberExpr): if not add_global: self.analyze_member_lvalue(lval, explicit_type, final_cb=final_cb) @@ -2015,11 +2031,13 @@ def analyze_name_lvalue(self, lval: NameExpr, add_global: bool, explicit_type: bool, - final_cb: Optional[Callable[[bool], None]]) -> None: + final_cb: Optional[Callable[[bool], None]], + has_initializer: bool) -> None: """Analyze an lvalue that targets a name expression. Arguments are similar to "analyze_lvalue". """ + is_new = self.var_def_analyzer.process_assignment(lval.name, has_initializer) # Top-level definitions within some statements (at least while) are # not handled in the first pass, so they have to be added now. nested_global = (not self.is_func_scope() and @@ -2035,12 +2053,14 @@ def analyze_name_lvalue(self, # already in the first pass and added to the symbol table. # An exception is typing module with incomplete test fixtures. assert lval.node.name() in self.globals or self.cur_mod_id == 'typing' - elif (self.locals[-1] is not None and lval.name not in self.locals[-1] and - lval.name not in self.global_decls[-1] and - lval.name not in self.nonlocal_decls[-1]): + elif (self.locals[-1] is not None + and (lval.name not in self.locals[-1] + or (is_new and not self.is_local_final(lval.name))) + and lval.name not in self.global_decls[-1] + and lval.name not in self.nonlocal_decls[-1]): # Define new local name. v = self.make_name_lvalue_var(lval, LDEF) - self.add_local(v, lval) + self.add_local(v, lval, allow_redefine=is_new) if lval.name == '_': # Special case for assignment to local named '_': always infer 'Any'. typ = AnyType(TypeOfAny.special_form) @@ -2054,6 +2074,9 @@ def analyze_name_lvalue(self, else: self.make_name_lvalue_point_to_existing_def(lval, explicit_type, final_cb) + def is_local_final(self, name: str) -> bool: + return is_final_node(self.locals[-1][name].node) + def make_name_lvalue_var(self, lvalue: NameExpr, kind: int) -> Var: """Return a Var node for an lvalue that is a name expression.""" v = Var(lvalue.name) @@ -3498,7 +3521,8 @@ def add_symbol(self, name: str, node: SymbolTableNode, # TODO: Combine these methods in the first and second pass into a single one. if self.is_func_scope(): assert self.locals[-1] is not None - if name in self.locals[-1]: + is_new = self.var_def_analyzer.process_assignment(name, can_be_redefined=False) + if name in self.locals[-1] and not is_new: # Flag redefinition unless this is a reimport of a module. if not (node.kind == MODULE_REF and self.locals[-1][name].node == node.node): @@ -3528,10 +3552,11 @@ def add_symbol(self, name: str, node: SymbolTableNode, return self.globals[name] = node - def add_local(self, node: Union[Var, FuncDef, OverloadedFuncDef], ctx: Context) -> None: + def add_local(self, node: Union[Var, FuncDef, OverloadedFuncDef], ctx: Context, + allow_redefine: bool = False) -> None: assert self.locals[-1] is not None, "Should not add locals outside a function" name = node.name() - if name in self.locals[-1]: + if name in self.locals[-1] and not allow_redefine: self.name_already_defined(name, ctx, self.locals[-1][name]) return node._fullname = name diff --git a/mypy/semanal_pass1.py b/mypy/semanal_pass1.py index 07c0f7cab204..bf1747a9185d 100644 --- a/mypy/semanal_pass1.py +++ b/mypy/semanal_pass1.py @@ -92,8 +92,10 @@ def visit_file(self, file: MypyFile, fnam: str, mod_id: str, options: Options) - v._fullname = self.sem.qualified_name(name) self.sem.globals[name] = SymbolTableNode(GDEF, v) + self.sem.var_def_analyzer.enter_block() for d in defs: d.accept(self) + self.sem.var_def_analyzer.leave_block() # Add implicit definition of literals/keywords to builtins, as we # cannot define a variable with them explicitly. @@ -138,8 +140,10 @@ def visit_block(self, b: Block) -> None: if b.is_unreachable: return self.sem.block_depth[-1] += 1 + self.sem.var_def_analyzer.enter_block() for node in b.body: node.accept(self) + self.sem.var_def_analyzer.leave_block() self.sem.block_depth[-1] -= 1 def visit_assignment_stmt(self, s: AssignmentStmt) -> None: diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index 359c1a3a0188..b342b8ddfb73 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -150,3 +150,68 @@ def set_callable_name(sig: Type, fdef: FuncDef) -> Type: return sig.with_name(fdef.name()) else: return sig + + +class VarDefAnalyzer: + """Helper class that keeps track of variable redefinitions. + + This decides whether an assignment to an existing variable should define a new variable. + This can happen if there are multiple assignments to a variable within the same block: + + x = 0 + x = str(x) # Defines a new 'x' + + Since we have two distinct 'x' variables, they can have independent inferred types. + """ + + def __init__(self) -> None: + self.block_id = 0 + self.blocks = [] # type: List[int] + self.var_blocks = {} # type: Dict[str, int] + + def clear(self) -> None: + self.blocks = [] + self.var_blocks = {} + + def enter_block(self) -> None: + self.block_id += 1 + self.blocks.append(self.block_id) + + def leave_block(self) -> None: + self.blocks.pop() + + def current_block(self) -> int: + return self.blocks[-1] + + def enter_scope(self) -> None: + pass # TODO + + def leave_scope(self) -> None: + pass # TODO + + def reject_redefinition_of_vars_in_scope(self) -> None: + """Make it impossible to redefine defined variables in the current scope. + + This is used if we encounter a function definition or break/continue that + can make it ambiguous which definition is live. + """ + for key in self.var_blocks: + self.var_blocks[key] = -1 + + def process_assignment(self, name: str, can_be_redefined: bool) -> bool: + """Record assignment to given name and return True if it defines a new name.""" + block = self.current_block() + if name not in self.var_blocks: + # New definition + if can_be_redefined: + self.var_blocks[name] = block + else: + # This doesn't support arbitrary redefinition. + # TODO: Make this less restricted. + self.var_blocks[name] = -1 + return True + elif self.var_blocks[name] == block: + # Redefinition + return True + else: + return False diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index ad15179cd684..08b16d9dd045 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -76,6 +76,7 @@ 'check-attr.test', 'check-dataclasses.test', 'check-final.test', + 'check-redefine.test', ] diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test index 8924c0943673..14653cf35b21 100644 --- a/test-data/unit/check-basic.test +++ b/test-data/unit/check-basic.test @@ -122,40 +122,34 @@ main:4: error: Too many arguments for "f" [case testLocalVariables] - def f() -> None: x = None # type: A y = None # type: B - x = x - x = y # Fail + if 1: + x = x + x = y # E: Incompatible types in assignment (expression has type "B", variable has type "A") class A: pass class B: pass -[out] -main:6: error: Incompatible types in assignment (expression has type "B", variable has type "A") [case testLocalVariableScope] - def f() -> None: - x = None # type: A + x: A x = A() def g() -> None: - x = None # type: B - x = A() # Fail + x: B + x = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") class A: pass class B: pass -[out] -main:7: error: Incompatible types in assignment (expression has type "A", variable has type "B") [case testFunctionArguments] import typing def f(x: 'A', y: 'B') -> None: - x = y # Fail - x = x - y = B() + if 1: + x = y # E: Incompatible types in assignment (expression has type "B", variable has type "A") + x = x + y = B() class A: pass class B: pass -[out] -main:3: error: Incompatible types in assignment (expression has type "B", variable has type "A") [case testLocalVariableInitialization] import typing @@ -245,21 +239,19 @@ a = __file__ # type: int # E: Incompatible types in assignment (expression has [case testLocalVariableShadowing] a = None # type: A -a = B() # Fail -a = A() +if 1: + a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + a = A() def f() -> None: a = None # type: B - a = A() # Fail - a = B() -a = B() # Fail + if 1: + a = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") + a = B() +a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") a = A() class A: pass class B: pass -[out] -main:3: error: Incompatible types in assignment (expression has type "B", variable has type "A") -main:7: error: Incompatible types in assignment (expression has type "A", variable has type "B") -main:9: error: Incompatible types in assignment (expression has type "B", variable has type "A") [case testGlobalDefinedInBlockWithType] diff --git a/test-data/unit/check-class-namedtuple.test b/test-data/unit/check-class-namedtuple.test index cf5bb46fc647..abc5689c71d5 100644 --- a/test-data/unit/check-class-namedtuple.test +++ b/test-data/unit/check-class-namedtuple.test @@ -149,9 +149,10 @@ class B(A): self.f(self.b) # E: Argument 1 to "f" of "B" has incompatible type "str"; expected "int" i = 0 s = '' - i, s = self - i, i = self # E: Incompatible types in assignment (expression has type "str", \ - variable has type "int") + if int(): + i, s = self + i, i = self # E: Incompatible types in assignment (expression has type "str", \ + variable has type "int") [out] [case testNewNamedTupleTypeReferenceToClassDerivedFrom] @@ -166,15 +167,14 @@ class B(A): def f(self, x: 'B') -> None: i = 0 s = '' - self = x - i, s = x - i, s = x.a, x.b - i, s = x.a, x.a # E: Incompatible types in assignment (expression has type "int", \ - variable has type "str") - i, i = self # E: Incompatible types in assignment (expression has type "str", \ - variable has type "int") - -[out] + if int(): + self = x + i, s = x + i, s = x.a, x.b + i, s = x.a, x.a # E: Incompatible types in assignment (expression has type "int", \ + variable has type "str") + i, i = self # E: Incompatible types in assignment (expression has type "str", \ + variable has type "int") [case testNewNamedTupleSubtyping] # flags: --python-version 3.6 diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 14ed21207a00..037572115d55 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -73,17 +73,15 @@ main:4: error: Incompatible types in assignment (expression has type "object", v import typing class A: def f(self, a: 'A', b: 'B') -> None: - a = B() # Fail - b = A() # Fail - a = A() - b = B() - a = a - a = b # Fail + if 1: + a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") + a = A() + b = B() + a = a + a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") class B: pass [out] -main:4: error: Incompatible types in assignment (expression has type "B", variable has type "A") -main:5: error: Incompatible types in assignment (expression has type "A", variable has type "B") -main:9: error: Incompatible types in assignment (expression has type "B", variable has type "A") [case testReturnFromMethod] import typing @@ -818,8 +816,9 @@ class A: class B: def __init__(self, a: 'A') -> None: pass b = B(A()) - b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") - b = B() # E: Too few arguments for "B" + if 1: + b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") + b = B() # E: Too few arguments for "B" [out] [case testDeclareVariableWithNestedClassType] @@ -827,8 +826,9 @@ class A: def f() -> None: class A: pass a = None # type: A - a = A() - a = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") + if 1: + a = A() + a = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") [out] [case testExternalReferenceToClassWithinClass] @@ -1828,7 +1828,7 @@ class Num1: class Num2(Num1): @overload - def __add__(self, other: Num2) -> Num2: ... + def __add__(self, other: Num2) -> Num2: ... @overload def __add__(self, other: Num1) -> Num2: ... def __add__(self, other): pass @@ -2966,19 +2966,20 @@ def new(uc: Type[U]) -> U: uc.foo() u = uc() u.foo() - u = uc(0) - u.foo() - u = uc('') - u.foo(0) - return uc() + if 1: + u = uc(0) + u.foo() + u = uc('') # Error + u.foo(0) # Error + return uc() u = new(User) [builtins fixtures/classmethod.pyi] [out] -tmp/foo.pyi:16: error: No overload variant of "User" matches argument type "str" -tmp/foo.pyi:16: note: Possible overload variant: -tmp/foo.pyi:16: note: def __init__(self, arg: int) -> U -tmp/foo.pyi:16: note: <1 more non-matching overload not shown> -tmp/foo.pyi:17: error: Too many arguments for "foo" of "User" +tmp/foo.pyi:17: error: No overload variant of "User" matches argument type "str" +tmp/foo.pyi:17: note: Possible overload variant: +tmp/foo.pyi:17: note: def __init__(self, arg: int) -> U +tmp/foo.pyi:17: note: <1 more non-matching overload not shown> +tmp/foo.pyi:18: error: Too many arguments for "foo" of "User" [case testTypeUsingTypeCInUpperBound] from typing import TypeVar, Type @@ -4254,8 +4255,9 @@ def parse_ast(name_dict: NameDict) -> None: pass x = name_dict[''] reveal_type(x) # E: Revealed type is '__main__.NameInfo*' - x = NameInfo(Base()) # OK - x = Base() # E: Incompatible types in assignment (expression has type "Base", variable has type "NameInfo") + if 1: + x = NameInfo(Base()) # OK + x = Base() # E: Incompatible types in assignment (expression has type "Base", variable has type "NameInfo") [builtins fixtures/isinstancelist.pyi] [out] diff --git a/test-data/unit/check-columns.test b/test-data/unit/check-columns.test index 44d9d6881a2e..89e7c4e1e5aa 100644 --- a/test-data/unit/check-columns.test +++ b/test-data/unit/check-columns.test @@ -57,12 +57,11 @@ main:4:15: error: Unsupported operand types for + ("str" and "int") # flags: --show-column-numbers import typing def f(x: object, n: int, s: str) -> None: - n = x # E:5: Incompatible types in assignment (expression has type "object", variable has type "int") - if isinstance(x, int): - n = x - s = x # E:9: Incompatible types in assignment (expression has type "int", variable has type "str") - n = x # E:5: Incompatible types in assignment (expression has type "object", variable has type "int") + if int(): + n = x # E:9: Incompatible types in assignment (expression has type "object", variable has type "int") + if isinstance(x, int): + n = x + s = x # E:13: Incompatible types in assignment (expression has type "int", variable has type "str") + n = x # E:9: Incompatible types in assignment (expression has type "object", variable has type "int") [builtins fixtures/isinstance.pyi] [out] - - diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 64936e30623b..6ab359241ecd 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -607,16 +607,15 @@ a.g(a) # E: Too many arguments import typing def f(a: 'A') -> None: def g(b: 'B') -> None: - b = a # fail - aa = a # type: A # ok - b = B() - g(a) # fail + if int(): + b = a \ + # E: Incompatible types in assignment (expression has type "A", variable has type "B") + aa = a # type: A # ok + b = B() + g(a) # E: Argument 1 to "g" has incompatible type "A"; expected "B" g(B()) class A: pass class B: pass -[out] -main:4: error: Incompatible types in assignment (expression has type "A", variable has type "B") -main:7: error: Argument 1 to "g" has incompatible type "A"; expected "B" [case testReturnAndNestedFunction] import typing @@ -752,7 +751,8 @@ def dec(f): pass @dec def f(x: 'A') -> None: a = x # type: A - x = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") + if int(): + x = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") class A: pass [out] @@ -1235,18 +1235,16 @@ x = None # type: Any def top() -> None: if x: def f(x: int) -> None: - x = 'x' # fail - x = 1 + if int(): + x = 'x' # E: Incompatible types in assignment \ + (expression has type "str", variable has type "int") + x = 1 else: def f(x: int) -> None: - x + 'x' # fail + x + 'x' # E: Unsupported operand types for + ("int" and "str") x = 1 f(1) - f('x') # fail -[out] -main:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") -main:10: error: Unsupported operand types for + ("int" and "str") -main:13: error: Argument 1 to "f" has incompatible type "str"; expected "int" + f('x') # E: Argument 1 to "f" has incompatible type "str"; expected "int" [case testUnconditionalRedefinitionOfConditionalFunction] from typing import Any @@ -1817,7 +1815,8 @@ f(x=4) + '' # E: Unsupported operand types for + ("int" and "str") [case testCallableWithArbitraryArgsInErrorMessage] from typing import Callable def f(x: Callable[..., int]) -> None: - x = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Callable[..., int]") + if int(): + x = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Callable[..., int]") [out] [case testCallableWithArbitraryArgsInGenericFunction] diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index e6fcda7ae6e2..54f7e5fbd434 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -218,19 +218,17 @@ class A(Generic[S, T]): def f(self) -> None: s = None # type: S t = None # type: T - s = t # Fail - t = s # Fail - a = self # type: A[S, B] # Fail - b = self # type: A[T, T] # Fail + if 1: + s = t # E: Incompatible types in assignment (expression has type "T", variable has type "S") + t = s # E: Incompatible types in assignment (expression has type "S", variable has type "T") + a = self # type: A[S, B] # E: Incompatible types in assignment (expression has type "A[S, T]", variable has type "A[S, B]") + b = self # type: A[T, T] # E: Incompatible types in assignment (expression has type "A[S, T]", variable has type "A[T, T]") c = self # type: A[S, T] - t = t + if 1: + t = t class B: pass [out] -main:8: error: Incompatible types in assignment (expression has type "T", variable has type "S") -main:9: error: Incompatible types in assignment (expression has type "S", variable has type "T") -main:10: error: Incompatible types in assignment (expression has type "A[S, T]", variable has type "A[S, B]") -main:11: error: Incompatible types in assignment (expression has type "A[S, T]", variable has type "A[T, T]") [case testCompatibilityOfNoneWithTypeVar] from typing import TypeVar, Generic @@ -246,14 +244,14 @@ from typing import TypeVar, Generic T = TypeVar('T') class A(Generic[T]): def f(self) -> T: - a = object() # type: T # Fail - a = object() # Fail + a = object() # type: T # E: Incompatible types in assignment (expression has type "object", variable has type "T") + if 1: + a = object() # E: Incompatible types in assignment (expression has type "object", variable has type "T") b = self.f() # type: object - b = self.f() + if 1: + b = self.f() return None [out] -main:5: error: Incompatible types in assignment (expression has type "object", variable has type "T") -main:6: error: Incompatible types in assignment (expression has type "object", variable has type "T") -- Operations with generic types @@ -381,7 +379,8 @@ class p(Generic[T, S]): def __init__(self, t: T, a: S) -> None: pass def f(s: S, t: T) -> p[T, A]: a = t # type: S # E: Incompatible types in assignment (expression has type "T", variable has type "S") - s = t # E: Incompatible types in assignment (expression has type "T", variable has type "S") + if 1: + s = t # E: Incompatible types in assignment (expression has type "T", variable has type "S") p_s_a = None # type: p[S, A] if s: return p_s_a # E: Incompatible return value type (got "p[S, A]", expected "p[T, A]") @@ -399,17 +398,19 @@ class p(Generic[T, S]): def __init__(self, t: T, a: S) -> None: pass class A(Generic[T]): def f(self, s: S, t: T) -> p[S, T]: - s = t # E: Incompatible types in assignment (expression has type "T", variable has type "S") + if 1: + s = t # E: Incompatible types in assignment (expression has type "T", variable has type "S") p_s_s = None # type: p[S, S] if s: return p_s_s # E: Incompatible return value type (got "p[S, S]", expected "p[S, T]") p_t_t = None # type: p[T, T] if t: return p_t_t # E: Incompatible return value type (got "p[T, T]", expected "p[S, T]") - t = t - s = s - p_s_t = None # type: p[S, T] - return p_s_t + if 1: + t = t + s = s + p_s_t = None # type: p[S, T] + return p_s_t [out] [case testProhibitTypeApplicationToGenericFunctions] @@ -1436,8 +1437,9 @@ def f() -> None: T = TypeVar('T') def g(x: T) -> T: pass a = g(1) - a = 1 - a = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") + if 1: + a = 1 + a = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") [out] [case testClassLevelTypeVariable] @@ -1536,31 +1538,36 @@ def outer(t: T) -> None: def f5(x: T) -> T: ... y1 = f1 - y1 = f2 - y1 = f3 # E: Incompatible types in assignment (expression has type "Callable[[T], A]", variable has type "Callable[[A], A]") - y1 = f4 # E: Incompatible types in assignment (expression has type "Callable[[A], T]", variable has type "Callable[[A], A]") - y1 = f5 # E: Incompatible types in assignment (expression has type "Callable[[T], T]", variable has type "Callable[[A], A]") + if 1: + y1 = f2 + y1 = f3 # E: Incompatible types in assignment (expression has type "Callable[[T], A]", variable has type "Callable[[A], A]") + y1 = f4 # E: Incompatible types in assignment (expression has type "Callable[[A], T]", variable has type "Callable[[A], A]") + y1 = f5 # E: Incompatible types in assignment (expression has type "Callable[[T], T]", variable has type "Callable[[A], A]") y2 = f2 - y2 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[A], B]") + if 1: + y2 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[A], B]") y3 = f3 - y3 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[T], A]") - y3 = f2 - y3 = f4 # E: Incompatible types in assignment (expression has type "Callable[[A], T]", variable has type "Callable[[T], A]") - y3 = f5 # E: Incompatible types in assignment (expression has type "Callable[[T], T]", variable has type "Callable[[T], A]") + if 1: + y3 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[T], A]") + y3 = f2 + y3 = f4 # E: Incompatible types in assignment (expression has type "Callable[[A], T]", variable has type "Callable[[T], A]") + y3 = f5 # E: Incompatible types in assignment (expression has type "Callable[[T], T]", variable has type "Callable[[T], A]") y4 = f4 - y4 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[A], T]") - y4 = f2 - y4 = f3 # E: Incompatible types in assignment (expression has type "Callable[[T], A]", variable has type "Callable[[A], T]") - y4 = f5 # E: Incompatible types in assignment (expression has type "Callable[[T], T]", variable has type "Callable[[A], T]") + if 1: + y4 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[A], T]") + y4 = f2 + y4 = f3 # E: Incompatible types in assignment (expression has type "Callable[[T], A]", variable has type "Callable[[A], T]") + y4 = f5 # E: Incompatible types in assignment (expression has type "Callable[[T], T]", variable has type "Callable[[A], T]") y5 = f5 - y5 = f1 - y5 = f2 - y5 = f3 - y5 = f4 + if 1: + y5 = f1 + y5 = f2 + y5 = f3 + y5 = f4 [out] [case testSubtypingWithGenericFunctionUsingTypevarWithValues] diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index f386890280b4..75fd1bff9592 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -1335,8 +1335,9 @@ from mod4 import C class B: def makeC(self) -> C: val = 3 # type: int - val = "str" # deliberately triggering error - return C() + if 1: + val = "str" # deliberately triggering error + return C() [file mod3.py.2] from mod4 import C @@ -1350,7 +1351,7 @@ class C: [rechecked mod3, mod2, mod1] [stale mod3, mod2] [out1] -tmp/mod3.py:5: error: Incompatible types in assignment (expression has type "str", variable has type "int") +tmp/mod3.py:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") tmp/mod1.py:3: error: Revealed type is 'builtins.int' [out2] @@ -1374,8 +1375,9 @@ from mod4 import C class B: def makeC(self) -> C: val = 3 # type: int - val = "str" # deliberately triggering error - return C() + if 1: + val = "str" # deliberately triggering error + return C() [file mod4.py] class C: @@ -1388,11 +1390,11 @@ class C: [rechecked mod4, mod3, mod2, mod1] [stale mod4] [out1] -tmp/mod3.py:5: error: Incompatible types in assignment (expression has type "str", variable has type "int") +tmp/mod3.py:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") tmp/mod1.py:3: error: Revealed type is 'builtins.int' [out2] -tmp/mod3.py:5: error: Incompatible types in assignment (expression has type "str", variable has type "int") +tmp/mod3.py:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") tmp/mod1.py:3: error: Revealed type is 'builtins.str' [case testIncrementalIncidentalChangeWithBugFixCausesPropagation] @@ -1413,8 +1415,9 @@ from mod4 import C class B: def makeC(self) -> C: val = 3 # type: int - val = "str" # deliberately triggering error - return C() + if 1: + val = "str" # deliberately triggering error + return C() [file mod3.py.2] from mod4 import C @@ -1432,7 +1435,7 @@ class C: [rechecked mod4, mod3, mod2, mod1] [stale mod4, mod3, mod2] [out1] -tmp/mod3.py:5: error: Incompatible types in assignment (expression has type "str", variable has type "int") +tmp/mod3.py:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") tmp/mod1.py:3: error: Revealed type is 'builtins.int' [out2] diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test index ffa203f51ed2..632cbeba0b42 100644 --- a/test-data/unit/check-inference-context.test +++ b/test-data/unit/check-inference-context.test @@ -74,11 +74,13 @@ def g() -> None: b = None # type: B x = f(o) - ab = x # E: Incompatible types in assignment (expression has type "A[object]", variable has type "A[B]") - ao = x + if 1: + ab = x # E: Incompatible types in assignment (expression has type "A[object]", variable has type "A[B]") + ao = x y = f(b) - ao = y # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") - ab = y + if 1: + ao = y # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") + ab = y def f(a: T) -> 'A[T]': pass @@ -104,10 +106,11 @@ def g() -> None: ab = None # type: A[B] b = None # type: B x, y = f(b), f(b) - ao = x # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") - ao = y # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") - ab = x - ab = y + if 1: + ao = x # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") + ao = y # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") + ab = x + ab = y def f(a: T) -> 'A[T]': pass class A(Generic[T]): pass @@ -122,10 +125,11 @@ def h() -> None: ab = None # type: A[B] b = None # type: B x, y = g(f(b)) - ao = x # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") - ao = y # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") - ab = x - ab = y + if 1: + ao = x # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") + ao = y # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") + ab = x + ab = y def f(a: T) -> 'A[T]': pass def g(a: T) -> List[T]: pass @@ -387,8 +391,9 @@ def f() -> None: a = [] # E: Need type annotation for 'a' b = [None] c = [B()] - c = [object()] # E: List item 0 has incompatible type "object"; expected "B" - c = [B()] + if 1: + c = [object()] # E: List item 0 has incompatible type "object"; expected "B" + c = [B()] class B: pass [builtins fixtures/list.pyi] [out] diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 67ebfa917f97..a3453f179158 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -21,15 +21,14 @@ import typing def f() -> None: x = A() y = B() - x = B() # Fail - x = A() - x = y # Fail - x = x + if 1: + x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + x = A() + x = y # E: Incompatible types in assignment (expression has type "B", variable has type "A") + x = x class A: pass class B: pass [out] -main:5: error: Incompatible types in assignment (expression has type "B", variable has type "A") -main:7: error: Incompatible types in assignment (expression has type "B", variable has type "A") [case testLvarInitializedToVoid] import typing @@ -44,9 +43,10 @@ def g() -> None: pass import typing def f(a: 'A') -> None: b = a - b = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") - b = a - a = b + if 1: + b = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + b = a + a = b class A: pass class B: pass @@ -58,8 +58,9 @@ g = None # type: B def f() -> None: a = g - a = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") - a = B() + if 1: + a = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") + a = B() class A: pass class B: pass @@ -96,10 +97,11 @@ def f() -> None: a = A(), B() aa = None # type: A bb = None # type: B - bb = a[0] # E: Incompatible types in assignment (expression has type "A", variable has type "B") - aa = a[1] # E: Incompatible types in assignment (expression has type "B", variable has type "A") - aa = a[0] - bb = a[1] + if 1: + bb = a[0] # E: Incompatible types in assignment (expression has type "A", variable has type "B") + aa = a[1] # E: Incompatible types in assignment (expression has type "B", variable has type "A") + aa = a[0] + bb = a[1] class A: pass class B: pass @@ -126,8 +128,9 @@ a_s = None # type: A[str] def f() -> None: a_int = A() # type: A[int] a = a_int - a = a_s # E: Incompatible types in assignment (expression has type "A[str]", variable has type "A[int]") - a = a_i + if 1: + a = a_s # E: Incompatible types in assignment (expression has type "A[str]", variable has type "A[int]") + a = a_i [builtins fixtures/tuple.pyi] [out] @@ -164,12 +167,13 @@ class A: pass import typing def f() -> None: a, b = A(), B() - a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") - a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") - b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") + if 1: + a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") + a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") - a = A() - b = B() + a = A() + b = B() class A: pass class B: pass @@ -180,12 +184,13 @@ from typing import Tuple def f() -> None: t = None # type: Tuple[A, B] a, b = t - a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") - a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") - b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") + if 1: + a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") + a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") - a = A() - b = B() + a = A() + b = B() class A: pass class B: pass @@ -196,12 +201,13 @@ from typing import Tuple def f() -> None: t = None # type: Tuple[A, B] a1, (a, b) = A(), t - a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") - a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") - b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") + if 1: + a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") + a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") - a = A() - b = B() + a = A() + b = B() class A: pass class B: pass @@ -211,14 +217,15 @@ class B: pass import typing def f() -> None: a, (b, c) = A(), (B(), C()) - a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") - a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") - b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") - c = A() # E: Incompatible types in assignment (expression has type "A", variable has type "C") + if 1: + a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") + a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") + c = A() # E: Incompatible types in assignment (expression has type "A", variable has type "C") - a = A() - b = B() - c = C() + a = A() + b = B() + c = C() class A: pass class B: pass @@ -229,14 +236,15 @@ class C: pass import typing def f() -> None: a, (b, c) = A(), [B(), C()] - a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") - a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") - b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") - c = A() # E: Incompatible types in assignment (expression has type "A", variable has type "C") + if 1: + a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") + a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") + c = A() # E: Incompatible types in assignment (expression has type "A", variable has type "C") - a = A() - b = B() - c = C() + a = A() + b = B() + c = C() class A: pass class B: pass @@ -302,20 +310,21 @@ def f() -> None: list_d = [D()] a, b = list_c c, d, e = list_d - a = D() # E: Incompatible types in assignment (expression has type "D", variable has type "C") - b = D() # E: Incompatible types in assignment (expression has type "D", variable has type "C") - c = C() # E: Incompatible types in assignment (expression has type "C", variable has type "D") - b = c # E: Incompatible types in assignment (expression has type "D", variable has type "C") - - a = C() - b = C() - c = D() - d = D() - e = D() - - a = b - c = d - d = e + if 1: + a = D() # E: Incompatible types in assignment (expression has type "D", variable has type "C") + b = D() # E: Incompatible types in assignment (expression has type "D", variable has type "C") + c = C() # E: Incompatible types in assignment (expression has type "C", variable has type "D") + b = c # E: Incompatible types in assignment (expression has type "D", variable has type "C") + + a = C() + b = C() + c = D() + d = D() + e = D() + + a = b + c = d + d = e [builtins fixtures/for.pyi] [out] @@ -330,20 +339,21 @@ def f() -> None: list_d = [D()] c1, (a, b) = C(), list_c c2, (c, d, e) = C(), list_d - a = D() # E: Incompatible types in assignment (expression has type "D", variable has type "C") - b = D() # E: Incompatible types in assignment (expression has type "D", variable has type "C") - c = C() # E: Incompatible types in assignment (expression has type "C", variable has type "D") - b = c # E: Incompatible types in assignment (expression has type "D", variable has type "C") - - a = C() - b = C() - c = D() - d = D() - e = D() - - a = b - c = d - d = e + if 1: + a = D() # E: Incompatible types in assignment (expression has type "D", variable has type "C") + b = D() # E: Incompatible types in assignment (expression has type "D", variable has type "C") + c = C() # E: Incompatible types in assignment (expression has type "C", variable has type "D") + b = c # E: Incompatible types in assignment (expression has type "D", variable has type "C") + + a = C() + b = C() + c = D() + d = D() + e = D() + + a = b + c = d + d = e [builtins fixtures/for.pyi] [out] @@ -406,9 +416,10 @@ def f() -> None: a = id b = None # type: int c = None # type: str - b = a(c) # E: Incompatible types in assignment (expression has type "str", variable has type "int") - b = a(b) - c = a(c) + if 1: + b = a(c) # E: Incompatible types in assignment (expression has type "str", variable has type "int") + b = a(b) + c = a(c) def id(x: T) -> T: return x [out] @@ -999,14 +1010,17 @@ class B: pass [builtins fixtures/for.pyi] [case testReusingInferredForIndex2] -import typing def f() -> None: for a in [A()]: pass a = A() - a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") - for a in []: pass + if 1: + a = B() \ + # E: Incompatible types in assignment (expression has type "B", variable has type "A") + for a in []: pass # E: Need type annotation for 'a' a = A() - a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + if 1: + a = B() \ + # E: Incompatible types in assignment (expression has type "B", variable has type "A") class A: pass class B: pass [builtins fixtures/for.pyi] diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index 3e155f25c0b8..7490bdf89f6c 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -133,10 +133,12 @@ x + 1 # E: Unsupported operand types for + ("str" and "int") from typing import Union def f(x: Union[int, str]) -> None: - x = 1 - if x: - x = 'a' + if 1: # Without this, the assignment below would create a new variable "x" of type "int" x = 1 + if x: + x = 'a' + x = 1 + x + 1 x + 1 [builtins fixtures/isinstancelist.pyi] @@ -2153,7 +2155,7 @@ def bar(x: Union[List[str], List[int], None]) -> None: from typing import Union, Optional, List # This test is the same as the one above, except for strict-optional. -# It isn't testing anything explicitly and mostly exists for the sake +# It isn't testing anything explicitly and mostly exists for the sake # of completeness. def foo(x: Optional[List[str]]) -> None: @@ -2166,4 +2168,3 @@ def bar(x: Union[List[str], List[int], None]) -> None: assert isinstance(x, list) reveal_type(x) # E: Revealed type is 'Union[builtins.list[builtins.str], builtins.list[builtins.int]]' [builtins fixtures/isinstancelist.pyi] - diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 9699fd084b55..5cf8d5fdfdee 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -80,12 +80,15 @@ def f() -> None: pass import typing def f() -> None: from m import a, b, f, A, B - a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") - a = a - f() - f(a) # E: Too many arguments for "f" - a = A() - a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + if 1: + a = b \ + # E: Incompatible types in assignment (expression has type "B", variable has type "A") + a = a + f() + f(a) # E: Too many arguments for "f" + a = A() + a = B() \ + # E: Incompatible types in assignment (expression has type "B", variable has type "A") [file m.py] class A: pass class B: pass @@ -99,12 +102,13 @@ import typing class C: def f(self) -> None: from m import * - a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") - a = a - f() - f(a) # E: Too many arguments for "f" - a = A() - a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + if 1: + a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") + a = a + f() + f(a) # E: Too many arguments for "f" + a = A() + a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") [file m.py] class A: pass class B: pass diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 8d2aa9abef4d..3f59d1a3536f 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -223,9 +223,10 @@ class B(A): self.f(self.b) # E: Argument 1 to "f" of "B" has incompatible type "str"; expected "int" i = 0 s = '' - i, s = self - i, i = self # E: Incompatible types in assignment (expression has type "str", \ - variable has type "int") + if 1: + i, s = self + i, i = self # E: Incompatible types in assignment (expression has type "str", \ + variable has type "int") [out] @@ -237,13 +238,14 @@ class B(A): def f(self, x: 'B') -> None: i = 0 s = '' - self = x - i, s = x - i, s = x.a, x.b - i, s = x.a, x.a # E: Incompatible types in assignment (expression has type "int", \ - variable has type "str") - i, i = self # E: Incompatible types in assignment (expression has type "str", \ - variable has type "int") + if 1: + self = x + i, s = x + i, s = x.a, x.b + i, s = x.a, x.a # E: Incompatible types in assignment (expression has type "int", \ + variable has type "str") + i, i = self # E: Incompatible types in assignment (expression has type "str", \ + variable has type "int") [out] @@ -542,11 +544,12 @@ import a def f(x: a.N) -> None: reveal_type(x) - x = a.N(1) - reveal_type(x) + if 1: + x = a.N(1) + reveal_type(x) [out] tmp/b.py:4: error: Revealed type is 'Tuple[Any, fallback=a.N]' -tmp/b.py:6: error: Revealed type is 'Tuple[Any, fallback=a.N]' +tmp/b.py:7: error: Revealed type is 'Tuple[Any, fallback=a.N]' [case testSimpleSelfReferentialNamedTuple] from typing import NamedTuple diff --git a/test-data/unit/check-newsyntax.test b/test-data/unit/check-newsyntax.test index b2fa8c807a84..61a012b49f82 100644 --- a/test-data/unit/check-newsyntax.test +++ b/test-data/unit/check-newsyntax.test @@ -75,7 +75,8 @@ strict2: int = None # E: Incompatible types in assignment (expression has type # flags: --strict-optional --python-version 3.6 def f() -> None: x: int - x = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") + if int(): + x = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") [out] [case testNewSyntaxWithStrictOptionalClasses] @@ -150,4 +151,3 @@ v = 1 reveal_type(f'{v}') # E: Revealed type is 'builtins.str' reveal_type(f'{1}') # E: Revealed type is 'builtins.str' [builtins fixtures/f_string.pyi] - diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index 955dd1cfe319..a682b56056e5 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -742,8 +742,9 @@ def g(x: Optional[int]) -> int: if x is not None: return x reveal_type(x) # E: Revealed type is 'None' - x = f() - reveal_type(x) # E: Revealed type is 'Union[builtins.int, Any]' - return x + if 1: + x = f() + reveal_type(x) # E: Revealed type is 'Union[builtins.int, Any]' + return x [builtins fixtures/bool.pyi] diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index ac56d09329ae..8924ffd30016 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -186,7 +186,8 @@ def f(x: 'B') -> 'A': ... def f(x: Any) -> Any: foo = 1 - foo = "bar" # E: Incompatible types in assignment (expression has type "str", variable has type "int") + if 1: + foo = "bar" # E: Incompatible types in assignment (expression has type "str", variable has type "int") @overload def g(x: 'A') -> 'B': ... @@ -195,7 +196,8 @@ def g(x: 'B') -> 'A': ... def g(x): foo = 1 - foo = "bar" + if 1: + foo = "bar" reveal_type(f(A())) # E: Revealed type is '__main__.B' reveal_type(f(B())) # E: Revealed type is '__main__.A' @@ -398,12 +400,14 @@ from foo import * from typing import overload @overload def f(x: 'A'): - x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") - x = A() + if 1: + x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + x = A() @overload def f(x: 'B'): - x = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") - x = B() + if 1: + x = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") + x = B() class A: pass class B: pass [out] @@ -415,12 +419,14 @@ from typing import overload class A: @overload def f(self, x: 'A'): - x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") - x = A() + if 1: + x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + x = A() @overload def f(self, x: 'B'): - x = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") - x = B() + if 1: + x = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") + x = B() class B: pass [out] diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test new file mode 100644 index 000000000000..90ecc5a6f5c9 --- /dev/null +++ b/test-data/unit/check-redefine.test @@ -0,0 +1,41 @@ +-- Redefinition of variable with a different type +-- ---------------------------------------------- + + +[case testRedefineLocalWithDifferentType] +def f() -> None: + x = 0 + reveal_type(x) # E: Revealed type is 'builtins.int' + x = '' + reveal_type(x) # E: Revealed type is 'builtins.str' + +[case testCannotConditionallyRedefineLocalWithDifferentType] +def f() -> None: + y = 0 + reveal_type(y) # E: Revealed type is 'builtins.int' + if int(): + y = '' \ + # E: Incompatible types in assignment (expression has type "str", variable has type "int") + reveal_type(y) # E: Revealed type is 'builtins.int' + reveal_type(y) # E: Revealed type is 'builtins.int' + +[case testRedefineFunctionArg] +def f(x: int) -> None: + x = '' + reveal_type(x) # E: Revealed type is 'builtins.str' +def g(x: int) -> None: + if int(): + x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") + reveal_type(x) # E: Revealed type is 'builtins.int' + +[case testCannotRedefineAnnotationOnly] +def f() -> None: + x: int + x = '' \ + # E: Incompatible types in assignment (expression has type "str", variable has type "int") + reveal_type(x) # E: Revealed type is 'builtins.int' +def g() -> None: + x: int # This syntax prevents redefinition through assignment + x = 1 + x = '' \ + # E: Incompatible types in assignment (expression has type "str", variable has type "int") diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index 850ec9ba6f38..7f9ff5f16e46 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -1436,8 +1436,9 @@ import typing def f() -> None: x = 1 y = 'x' - x = y = 'x' # E: Incompatible types in assignment (expression has type "str", variable has type "int") - x = y = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "str") + if 1: + x = y = 'x' # E: Incompatible types in assignment (expression has type "str", variable has type "int") + x = y = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "str") [builtins fixtures/primitives.pyi] [out] diff --git a/test-data/unit/check-super.test b/test-data/unit/check-super.test index 73ac92a17d91..b50c8242f57a 100644 --- a/test-data/unit/check-super.test +++ b/test-data/unit/check-super.test @@ -12,9 +12,10 @@ class B: class A(B): def f(self) -> 'A': a, b = None, None # type: (A, B) - a = super().f() # E: Incompatible types in assignment (expression has type "B", variable has type "A") - a = super().g() # E: "g" undefined in superclass - b = super().f() + if 1: + a = super().f() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + a = super().g() # E: "g" undefined in superclass + b = super().f() return a [out] diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 8e39d88325db..7a1316059cd7 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -255,16 +255,17 @@ def avoid_confusing_test_parser() -> None: reveal_type(a1) # E: Revealed type is '__main__.A' reveal_type(b1) # E: Revealed type is '__main__.B' - [a, a] = t1 # E: Incompatible types in assignment (expression has type "B", variable has type "A") - [b, b] = t1 # E: Incompatible types in assignment (expression has type "A", variable has type "B") - [a, b, b] = t2 # E: Incompatible types in assignment (expression has type "A", variable has type "B") + if int(): + [a, a] = t1 # E: Incompatible types in assignment (expression has type "B", variable has type "A") + [b, b] = t1 # E: Incompatible types in assignment (expression has type "A", variable has type "B") + [a, b, b] = t2 # E: Incompatible types in assignment (expression has type "A", variable has type "B") - [a, b] = t1 - [a, b, a1] = t2 + [a, b] = t1 + [a, b, a1] = t2 - [a2, b2] = t1 - reveal_type(a2) # E: Revealed type is '__main__.A' - reveal_type(b2) # E: Revealed type is '__main__.B' + [a2, b2] = t1 + reveal_type(a2) # E: Revealed type is '__main__.A' + reveal_type(b2) # E: Revealed type is '__main__.B' class A: pass class B: pass @@ -284,16 +285,17 @@ def avoid_confusing_test_parser(): reveal_type(a1) # E: Revealed type is '__main__.A' reveal_type(b1) # E: Revealed type is '__main__.B' - [a, a] = t1 # E: Incompatible types in assignment (expression has type "B", variable has type "A") - [b, b] = t1 # E: Incompatible types in assignment (expression has type "A", variable has type "B") - [a, b, b] = t2 # E: Incompatible types in assignment (expression has type "A", variable has type "B") + if int(): + [a, a] = t1 # E: Incompatible types in assignment (expression has type "B", variable has type "A") + [b, b] = t1 # E: Incompatible types in assignment (expression has type "A", variable has type "B") + [a, b, b] = t2 # E: Incompatible types in assignment (expression has type "A", variable has type "B") - [a, b] = t1 - [a, b, a1] = t2 + [a, b] = t1 + [a, b, a1] = t2 - [a2, b2] = t1 - reveal_type(a2) # E: Revealed type is '__main__.A' - reveal_type(b2) # E: Revealed type is '__main__.B' + [a2, b2] = t1 + reveal_type(a2) # E: Revealed type is '__main__.A' + reveal_type(b2) # E: Revealed type is '__main__.B' class A: pass class B: pass @@ -754,14 +756,15 @@ from typing import Tuple class A(Tuple[int, str]): def f(self, x: int) -> None: a, b = 1, '' - a, b = self - b, a = self # Error + if int(): + a, b = self + b, a = self # Error self.f('') # Error [builtins fixtures/tuple.pyi] [out] -tmp/m.pyi:6: error: Incompatible types in assignment (expression has type "int", variable has type "str") -tmp/m.pyi:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") -tmp/m.pyi:7: error: Argument 1 to "f" of "A" has incompatible type "str"; expected "int" +tmp/m.pyi:7: error: Incompatible types in assignment (expression has type "int", variable has type "str") +tmp/m.pyi:7: error: Incompatible types in assignment (expression has type "str", variable has type "int") +tmp/m.pyi:8: error: Argument 1 to "f" of "A" has incompatible type "str"; expected "int" [case testValidTupleBaseClass2] from typing import Tuple diff --git a/test-data/unit/check-type-checks.test b/test-data/unit/check-type-checks.test index dc3265238794..eead1ff968c0 100644 --- a/test-data/unit/check-type-checks.test +++ b/test-data/unit/check-type-checks.test @@ -16,11 +16,12 @@ n = x # E: Incompatible types in assignment (expression has type "object", varia [case testSimpleIsinstance2] import typing def f(x: object, n: int, s: str) -> None: - n = x # E: Incompatible types in assignment (expression has type "object", variable has type "int") - if isinstance(x, int): - n = x - s = x # E: Incompatible types in assignment (expression has type "int", variable has type "str") - n = x # E: Incompatible types in assignment (expression has type "object", variable has type "int") + if 1: + n = x # E: Incompatible types in assignment (expression has type "object", variable has type "int") + if isinstance(x, int): + n = x + s = x # E: Incompatible types in assignment (expression has type "int", variable has type "str") + n = x # E: Incompatible types in assignment (expression has type "object", variable has type "int") [builtins fixtures/isinstance.pyi] [out] @@ -73,28 +74,30 @@ def f(x: object, y: object, n: int, s: str) -> None: [case testIsinstanceAndElif] import typing def f(x: object, n: int, s: str) -> None: - n = x # E: Incompatible types in assignment (expression has type "object", variable has type "int") - if isinstance(x, int): - n = x - s = x # E: Incompatible types in assignment (expression has type "int", variable has type "str") - elif isinstance(x, str): - s = x - n = x # E: Incompatible types in assignment (expression has type "str", variable has type "int") - else: + if 1: + n = x # E: Incompatible types in assignment (expression has type "object", variable has type "int") + if isinstance(x, int): + n = x + s = x # E: Incompatible types in assignment (expression has type "int", variable has type "str") + elif isinstance(x, str): + s = x + n = x # E: Incompatible types in assignment (expression has type "str", variable has type "int") + else: + n = x # E: Incompatible types in assignment (expression has type "object", variable has type "int") + s = x # E: Incompatible types in assignment (expression has type "object", variable has type "str") n = x # E: Incompatible types in assignment (expression has type "object", variable has type "int") - s = x # E: Incompatible types in assignment (expression has type "object", variable has type "str") - n = x # E: Incompatible types in assignment (expression has type "object", variable has type "int") [builtins fixtures/isinstance.pyi] [out] [case testIsinstanceAndAnyType] from typing import Any def f(x: Any, n: int, s: str) -> None: - s = x - if isinstance(x, int): - n = x - s = x # E: Incompatible types in assignment (expression has type "int", variable has type "str") - s = x + if 1: + s = x + if isinstance(x, int): + n = x + s = x # E: Incompatible types in assignment (expression has type "int", variable has type "str") + s = x [builtins fixtures/isinstance.pyi] [out] diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index e989a67965a6..7ae6dbccafce 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -818,9 +818,10 @@ from mypy_extensions import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) def f(p: Point) -> None: - p = {'x': 2, 'y': 3} - p = {'x': 2} # E: Key 'y' missing for TypedDict "Point" - p = dict(x=2, y=3) + if 1: + p = {'x': 2, 'y': 3} + p = {'x': 2} # E: Key 'y' missing for TypedDict "Point" + p = dict(x=2, y=3) f({'x': 1, 'y': 3}) f({'x': 1, 'y': 'z'}) # E: Incompatible types (expression has type "str", TypedDict item "y" has type "int") diff --git a/test-data/unit/check-typevar-values.test b/test-data/unit/check-typevar-values.test index 43ab8f85a7eb..b8eae158e60f 100644 --- a/test-data/unit/check-typevar-values.test +++ b/test-data/unit/check-typevar-values.test @@ -95,11 +95,12 @@ T = TypeVar('T', int, str) def f(x: T) -> T: a = None # type: T b = None # type: T - a = x - b = x - a = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") - b = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "str") - return x + if 1: + a = x + b = x + a = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") + b = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "str") + return x [out] [case testIsinstanceAndTypeVarValues] @@ -414,15 +415,16 @@ class C(Generic[Y]): def f(x: X, y: Y, z: int) -> None: C(y) C(x) # Error - z = x # Error - z = y # Error + if 1: + z = x # Error + z = y # Error y.foo # Error [out] main:8: error: Value of type variable "Y" of "C" cannot be "X" -main:9: error: Incompatible types in assignment (expression has type "X", variable has type "int") -main:10: error: Incompatible types in assignment (expression has type "str", variable has type "int") -main:11: error: "int" has no attribute "foo" -main:11: error: "str" has no attribute "foo" +main:10: error: Incompatible types in assignment (expression has type "X", variable has type "int") +main:11: error: Incompatible types in assignment (expression has type "str", variable has type "int") +main:12: error: "int" has no attribute "foo" +main:12: error: "str" has no attribute "foo" [case testTypeVarWithValueInferredFromObjectReturnTypeContext] from typing import TypeVar @@ -497,8 +499,9 @@ from typing import TypeVar, overload, Callable T = TypeVar('T', int, str) def f(x: T) -> None: y = m(g, x) - x = y - y = object() + if 1: + x = y + y = object() # Error A = TypeVar('A') R = TypeVar('R') @@ -509,8 +512,8 @@ def g(x: int) -> int: return x @overload def g(x: str) -> str: return x [out] -tmp/foo.pyi:7: error: Incompatible types in assignment (expression has type "object", variable has type "int") -tmp/foo.pyi:7: error: Incompatible types in assignment (expression has type "object", variable has type "str") +tmp/foo.pyi:8: error: Incompatible types in assignment (expression has type "object", variable has type "int") +tmp/foo.pyi:8: error: Incompatible types in assignment (expression has type "object", variable has type "str") [case testGenericFunctionSubtypingWithTypevarValues] from typing import TypeVar diff --git a/test-data/unit/check-unions.test b/test-data/unit/check-unions.test index 1cb274c2c4a8..75aea83de97e 100644 --- a/test-data/unit/check-unions.test +++ b/test-data/unit/check-unions.test @@ -27,10 +27,12 @@ from typing import Union def f(x: Union[int, str]) -> None: if isinstance(x, int): y = 1 - y = x + if 1: + y = x else: z = 2 - z = x # E: Incompatible types in assignment (expression has type "str", variable has type "int") + if 1: + z = x # E: Incompatible types in assignment (expression has type "str", variable has type "int") [builtins fixtures/isinstance.pyi] [out] diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index 988038264d54..38483d0fa354 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -581,13 +581,14 @@ class Child(Parent): return 3 def bar(self) -> int: - self = super(Child, self).something() - reveal_type(self) # E: Revealed type is '__main__.Child' - if self is None: - reveal_type(self) - return None - reveal_type(self) # E: Revealed type is '__main__.Child' - return 3 + if 1: + self = super(Child, self).something() + reveal_type(self) # E: Revealed type is '__main__.Child' + if self is None: + reveal_type(self) + return None + reveal_type(self) # E: Revealed type is '__main__.Child' + return 3 [builtins fixtures/isinstance.pyi] [case testUnreachableWhenSuperclassIsAnyNoStrictOptional] diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index 81701f8eea01..b161f5584e51 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -10,10 +10,11 @@ from typing import Tuple def f( *b: 'B') -> None: ab = None # type: Tuple[B, ...] ac = None # type: Tuple[C, ...] - b = ac # E: Incompatible types in assignment (expression has type "Tuple[C, ...]", variable has type "Tuple[B, ...]") - ac = b # E: Incompatible types in assignment (expression has type "Tuple[B, ...]", variable has type "Tuple[C, ...]") - b = ab - ab = b + if int(): + b = ac # E: Incompatible types in assignment (expression has type "Tuple[C, ...]", variable has type "Tuple[B, ...]") + ac = b # E: Incompatible types in assignment (expression has type "Tuple[B, ...]", variable has type "Tuple[C, ...]") + b = ab + ab = b class B: pass class C: pass diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index ba64993fedde..372e4332e682 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -664,14 +664,16 @@ import m class B(m.A): def a(self) -> None: x = 1 - x = self.x + if int(): + x = self.x def f(self) -> None: self.x = 1 def z(self) -> None: x = 1 - x = self.x + if int(): + x = self.x [file m.py] class A: pass [file m.py.2] @@ -680,9 +682,9 @@ class A: self.x = 'a' [out] == -main:5: error: Incompatible types in assignment (expression has type "str", variable has type "int") -main:8: error: Incompatible types in assignment (expression has type "int", variable has type "str") -main:12: error: Incompatible types in assignment (expression has type "str", variable has type "int") +main:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") +main:9: error: Incompatible types in assignment (expression has type "int", variable has type "str") +main:14: error: Incompatible types in assignment (expression has type "str", variable has type "int") [case testChangeBaseClassAttributeType] import m @@ -836,14 +838,15 @@ m/__init__.py:3: error: Too few arguments for "g" import m def f() -> None: x = 1 - x = m.x + if int(): + x = m.x [file m.py] x = 1 [file m.py.2] x = '' [out] == -main:4: error: Incompatible types in assignment (expression has type "str", variable has type "int") +main:5: error: Incompatible types in assignment (expression has type "str", variable has type "int") [case testTwoStepsDueToModuleAttribute] import m @@ -851,37 +854,40 @@ x = m.f() def g() -> None: y = 1 - y = x # E + if int(): + y = x # E [file m.py] def f() -> int: pass [file m.py.2] def f() -> str: pass [out] == -main:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") +main:7: error: Incompatible types in assignment (expression has type "str", variable has type "int") [case testTwoStepsDueToMultipleNamespaces] import m - x = m.f() def g() -> None: xx = 1 - xx = x + if int(): + xx = x # E class A: def a(self) -> None: self.y = m.f() def b(self) -> None: yy = 1 - yy = self.y + if int(): + yy = self.y class B: def c(self) -> None: self.z = m.f() def b(self) -> None: zz = 1 - zz = self.z + if int(): + zz = self.z [file m.py] def f() -> int: pass [file m.py.2] @@ -889,8 +895,8 @@ def f() -> str: pass [out] == main:7: error: Incompatible types in assignment (expression has type "str", variable has type "int") -main:14: error: Incompatible types in assignment (expression has type "str", variable has type "int") -main:21: error: Incompatible types in assignment (expression has type "str", variable has type "int") +main:15: error: Incompatible types in assignment (expression has type "str", variable has type "int") +main:23: error: Incompatible types in assignment (expression has type "str", variable has type "int") [case testConstructorSignatureChanged] import m @@ -1048,14 +1054,15 @@ from m import x def f() -> None: y = 1 - y = x + if int(): + y = x [file m.py] x = 1 [file m.py.2] x = '' [out] == -main:5: error: Incompatible types in assignment (expression has type "str", variable has type "int") +main:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") [case testImportFromSubmoduleOfPackage] from m import n @@ -1108,7 +1115,8 @@ from m import A def f(x: A.B) -> None: z = 1 - z = x.y + if int(): + z = x.y [file m.py] class A: class B: @@ -1121,7 +1129,7 @@ class A: self.y = '' [out] == -main:5: error: Incompatible types in assignment (expression has type "str", variable has type "int") +main:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") [case testReprocessMethodInNestedClass] from m import f @@ -1130,7 +1138,8 @@ class A: class B: def g(self) -> None: x = 1 - x = f() + if int(): + x = f() [file m.py] def f() -> int: pass [file m.py.2] @@ -1138,9 +1147,9 @@ def f() -> str: pass [file n.py.3] [out] == -main:7: error: Incompatible types in assignment (expression has type "str", variable has type "int") +main:8: error: Incompatible types in assignment (expression has type "str", variable has type "int") == -main:7: error: Incompatible types in assignment (expression has type "str", variable has type "int") +main:8: error: Incompatible types in assignment (expression has type "str", variable has type "int") [case testReprocessMethodInNestedClassSemanal] import a @@ -3144,10 +3153,11 @@ A = str [file b.py] import a def f(x: a.A): - x = int() + if int(): + x = int() [out] == -b.py:3: error: Incompatible types in assignment (expression has type "int", variable has type "str") +b.py:4: error: Incompatible types in assignment (expression has type "int", variable has type "str") [case testAliasFineNormalClass] import b @@ -3240,11 +3250,12 @@ def f(x: A): A = int [file b.py.2] def f(x: A): - x = int() + if int(): + x = int() A = str [out] == -b.py:2: error: Incompatible types in assignment (expression has type "int", variable has type "str") +b.py:3: error: Incompatible types in assignment (expression has type "int", variable has type "str") [case testAliasFineChainedFunc] import b @@ -3258,10 +3269,11 @@ B = a.A [file b.py] import aa def f(x: aa.B): - x = int() + if int(): + x = int() [out] == -b.py:3: error: Incompatible types in assignment (expression has type "int", variable has type "str") +b.py:4: error: Incompatible types in assignment (expression has type "int", variable has type "str") [case testAliasFineChainedClass] import b @@ -3296,11 +3308,12 @@ import a B = Dict[str, a.A] [file b.py] import aa + x: aa.B = {'first': {str(): int()}} [builtins fixtures/dict.pyi] [out] == -b.py:2: error: Dict entry 0 has incompatible type "str": "int"; expected "str": "str" +b.py:3: error: Dict entry 0 has incompatible type "str": "int"; expected "str": "str" [case testAliasFineNestedFunc] import b @@ -3317,11 +3330,12 @@ B = Dict[str, a.A] [file b.py] import aa def f(x: aa.B): - x = {'first': {str(): int()}} + if int(): + x = {'first': {str(): int()}} [builtins fixtures/dict.pyi] [out] == -b.py:3: error: Dict entry 0 has incompatible type "str": "int"; expected "str": "str" +b.py:4: error: Dict entry 0 has incompatible type "str": "int"; expected "str": "str" [case testAliasFineNestedFuncDirect] import b @@ -3338,11 +3352,12 @@ E = Dict [file b.py] import aa def f(x: aa.E[str, aa.a.A]): - x = {'first': {str(): int()}} + if int(): + x = {'first': {str(): int()}} [builtins fixtures/dict.pyi] [out] == -b.py:3: error: Dict entry 0 has incompatible type "str": "int"; expected "str": "str" +b.py:4: error: Dict entry 0 has incompatible type "str": "int"; expected "str": "str" [case testAliasFineNonGenericToGeneric] import b @@ -6698,7 +6713,8 @@ from a import f def g() -> Iterator[int]: a = "string" - a = yield from f() + if int(): + a = yield from f() [file a.py] from typing import Generator @@ -6718,7 +6734,7 @@ def f() -> Generator[int, None, A]: [out] == -main:6: error: Incompatible types in assignment (expression has type "A", variable has type "str") +main:7: error: Incompatible types in assignment (expression has type "A", variable has type "str") [case testFString] from a import g From 3b409fc4d3b3f53cb5884d3ae9aa717c93b8671f Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 20 Sep 2018 11:23:52 +0100 Subject: [PATCH 02/47] Add docstring --- mypy/semanal.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypy/semanal.py b/mypy/semanal.py index 3fc9aab98bf6..d20ac1e7ae67 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2425,6 +2425,7 @@ def parse_bool(self, expr: Expression) -> Optional[bool]: return None def check_classvar(self, s: AssignmentStmt) -> None: + """Check if assignment defines a class variable.""" lvalue = s.lvalues[0] if len(s.lvalues) != 1 or not isinstance(lvalue, RefExpr): return From 71cb8ea1a313fab5df2c58bd519563bd6852ba44 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 20 Sep 2018 11:24:04 +0100 Subject: [PATCH 03/47] Allow redefinitions of form "x = f(x)" Previously the `x` reference in the rvalue had type `Any`. --- mypy/semanal.py | 2 +- test-data/unit/check-redefine.test | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index d20ac1e7ae67..5952db8026b3 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1707,6 +1707,7 @@ def add_type_alias_deps(self, aliases_used: Iterable[str], def visit_assignment_stmt(self, s: AssignmentStmt) -> None: self.unwrap_final(s) + s.rvalue.accept(self) def final_cb(keep_final: bool) -> None: self.fail("Cannot redefine an existing name as final", s) @@ -1720,7 +1721,6 @@ def final_cb(keep_final: bool) -> None: has_initializer=has_initializer) self.check_final_implicit_def(s) self.check_classvar(s) - s.rvalue.accept(self) if s.type: allow_tuple_literal = isinstance(s.lvalues[-1], TupleExpr) s.type = self.anal_type(s.type, allow_tuple_literal=allow_tuple_literal) diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index 90ecc5a6f5c9..defb30b8e71f 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -39,3 +39,17 @@ def g() -> None: x = 1 x = '' \ # E: Incompatible types in assignment (expression has type "str", variable has type "int") + +[case testRedefineLocalUsingOldValue] +from typing import TypeVar, Union + +T = TypeVar('T') + +def f(x: int) -> None: + x = g(x) + reveal_type(x) # E: Revealed type is 'Union[builtins.int*, builtins.str]' + y = 1 + y = g(y) + reveal_type(y) # E: Revealed type is 'Union[builtins.int*, builtins.str]' + +def g(x: T) -> Union[T, str]: pass From 35f56ae983e744325b60026b9865818aba8eb6e5 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 20 Sep 2018 12:13:55 +0100 Subject: [PATCH 04/47] Fix special cases --- mypy/semanal.py | 12 ++++ mypy/semanal_shared.py | 31 +++++--- test-data/unit/check-redefine.test | 112 ++++++++++++++++++++++++++--- 3 files changed, 137 insertions(+), 18 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 5952db8026b3..57b52bc53297 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -373,9 +373,17 @@ def file_context(self, file_node: MypyFile, fnam: str, options: Options, def visit_func_def(self, defn: FuncDef) -> None: if not self.recurse_into_functions: return + + # Conservatively do not allow variable defined before a function to + # be redefined later, since function could refer to either definition. + self.var_def_analyzer.reject_redefinition_of_vars_in_scope() + self.var_def_analyzer.enter_scope() + with self.scope.function_scope(defn): self._visit_func_def(defn) + self.var_def_analyzer.leave_scope() + def _visit_func_def(self, defn: FuncDef) -> None: phase_info = self.postpone_nested_functions_stack[-1] if phase_info != FUNCTION_SECOND_PHASE: @@ -2666,7 +2674,9 @@ def visit_try_stmt(self, s: TryStmt) -> None: def analyze_try_stmt(self, s: TryStmt, visitor: NodeVisitor[None], add_global: bool = False) -> None: + self.var_def_analyzer.enter_with_or_try() s.body.accept(visitor) + self.var_def_analyzer.leave_with_or_try() for type, var, handler in zip(s.types, s.vars, s.handlers): if type: type.accept(visitor) @@ -2723,7 +2733,9 @@ def visit_with_stmt(self, s: WithStmt) -> None: elif isinstance(s.target_type, TupleType): s.target_type = s.target_type.copy_modified(items=new_types) + self.var_def_analyzer.enter_with_or_try() self.visit_block(s.body) + self.var_def_analyzer.leave_with_or_try() def visit_del_stmt(self, s: DelStmt) -> None: s.expr.accept(self) diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index b342b8ddfb73..42c43844c4a5 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -166,12 +166,13 @@ class VarDefAnalyzer: def __init__(self) -> None: self.block_id = 0 + self.disallow_redef_depth = 0 self.blocks = [] # type: List[int] - self.var_blocks = {} # type: Dict[str, int] + self.var_blocks = [{}] # type: List[Dict[str, int]] def clear(self) -> None: self.blocks = [] - self.var_blocks = {} + self.var_blocks = [{}] def enter_block(self) -> None: self.block_id += 1 @@ -180,14 +181,20 @@ def enter_block(self) -> None: def leave_block(self) -> None: self.blocks.pop() + def enter_with_or_try(self) -> None: + self.disallow_redef_depth += 1 + + def leave_with_or_try(self) -> None: + self.disallow_redef_depth -= 1 + def current_block(self) -> int: return self.blocks[-1] def enter_scope(self) -> None: - pass # TODO + self.var_blocks.append({}) def leave_scope(self) -> None: - pass # TODO + self.var_blocks.pop() def reject_redefinition_of_vars_in_scope(self) -> None: """Make it impossible to redefine defined variables in the current scope. @@ -195,22 +202,26 @@ def reject_redefinition_of_vars_in_scope(self) -> None: This is used if we encounter a function definition or break/continue that can make it ambiguous which definition is live. """ - for key in self.var_blocks: - self.var_blocks[key] = -1 + var_blocks = self.var_blocks[-1] + for key in var_blocks: + var_blocks[key] = -1 def process_assignment(self, name: str, can_be_redefined: bool) -> bool: """Record assignment to given name and return True if it defines a new name.""" + if self.disallow_redef_depth > 0: + can_be_redefined = False block = self.current_block() - if name not in self.var_blocks: + var_blocks = self.var_blocks[-1] + if name not in var_blocks: # New definition if can_be_redefined: - self.var_blocks[name] = block + var_blocks[name] = block else: # This doesn't support arbitrary redefinition. # TODO: Make this less restricted. - self.var_blocks[name] = -1 + var_blocks[name] = -1 return True - elif self.var_blocks[name] == block: + elif var_blocks[name] == block: # Redefinition return True else: diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index defb30b8e71f..9217a516b9d3 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -1,23 +1,26 @@ --- Redefinition of variable with a different type --- ---------------------------------------------- +-- Test cases for the redefinition of variable with a different type. + + +-- Redefine local variable +-- ----------------------- [case testRedefineLocalWithDifferentType] def f() -> None: x = 0 - reveal_type(x) # E: Revealed type is 'builtins.int' + reveal_type(x) # E: Revealed type is 'builtins.int' x = '' - reveal_type(x) # E: Revealed type is 'builtins.str' + reveal_type(x) # E: Revealed type is 'builtins.str' [case testCannotConditionallyRedefineLocalWithDifferentType] def f() -> None: y = 0 - reveal_type(y) # E: Revealed type is 'builtins.int' + reveal_type(y) # E: Revealed type is 'builtins.int' if int(): y = '' \ # E: Incompatible types in assignment (expression has type "str", variable has type "int") reveal_type(y) # E: Revealed type is 'builtins.int' - reveal_type(y) # E: Revealed type is 'builtins.int' + reveal_type(y) # E: Revealed type is 'builtins.int' [case testRedefineFunctionArg] def f(x: int) -> None: @@ -33,9 +36,9 @@ def f() -> None: x: int x = '' \ # E: Incompatible types in assignment (expression has type "str", variable has type "int") - reveal_type(x) # E: Revealed type is 'builtins.int' + reveal_type(x) # E: Revealed type is 'builtins.int' def g() -> None: - x: int # This syntax prevents redefinition through assignment + x: int # This syntax prevents redefinition through assignment x = 1 x = '' \ # E: Incompatible types in assignment (expression has type "str", variable has type "int") @@ -53,3 +56,96 @@ def f(x: int) -> None: reveal_type(y) # E: Revealed type is 'Union[builtins.int*, builtins.str]' def g(x: T) -> Union[T, str]: pass + +[case testRedefineLocalForLoopIndexVariable] +from typing import Iterable +def f(a: Iterable[int], b: Iterable[str]) -> None: + for x in a: + x = '' \ + # E: Incompatible types in assignment (expression has type "str", variable has type "int") + reveal_type(x) # E: Revealed type is 'builtins.int*' + for x in b: + x = 1 \ + # E: Incompatible types in assignment (expression has type "int", variable has type "str") + reveal_type(x) # E: Revealed type is 'builtins.str*' + +def g(a: Iterable[int]) -> None: + for x in a: pass + x = '' + +def h(a: Iterable[int]) -> None: + x = '' + for x in a: pass + +[case testCannotRedefineLocalWithinTry] +def f() -> None: + try: + x = 0 + g() # Might raise an exception + x = '' \ + # E: Incompatible types in assignment (expression has type "str", variable has type "int") + except: + pass + reveal_type(x) # E: Revealed type is 'builtins.int' + y = 0 + y = '' + +def g(): pass + +[case testCannotRedefineLocalWithinWith] +def f() -> None: + with g(): + x = 0 + g() # Might raise an exception + x = '' \ + # E: Incompatible types in assignment (expression has type "str", variable has type "int") + reveal_type(x) # E: Revealed type is 'builtins.int' + y = 0 + y = '' + +def g(): pass + +[case testCannotRedefineAcrossNestedFunction] +def f() -> None: + x = 0 + def g() -> None: + x + g() + x = '' \ + # E: Incompatible types in assignment (expression has type "str", variable has type "int") + g() + y = 0 + y = '' + +[case testCannotRedefineAcrossNestedDecoratedFunction] +def dec(f): return f + +def f() -> None: + x = 0 + @dec + def g() -> None: + x + g() + x = '' \ + # E: Incompatible types in assignment (expression has type "str", variable has type "int") + g() + y = 0 + y = '' + +[case testCannotRedefineAcrossNestedOverloadedFunction] +from typing import overload + +def f() -> None: + x = 0 + @overload + def g() -> None: pass + @overload + def g(x: int) -> None: pass + def g(x=0): + pass + g() + x = '' \ + # E: Incompatible types in assignment (expression has type "str", variable has type "int") + g() + y = 0 + y = '' From 2524777f1c455162165741761cbce2985191c0d0 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 20 Sep 2018 12:29:46 +0100 Subject: [PATCH 05/47] Add test cases --- test-data/unit/check-redefine.test | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index 9217a516b9d3..301186c9f644 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -149,3 +149,23 @@ def f() -> None: g() y = 0 y = '' + +[case testRedefineLocalInMultipleAssignment] +def f() -> None: + x, x = 1, '' + reveal_type(x) # E: Revealed type is 'builtins.str' + x = object() + reveal_type(x) # E: Revealed type is 'builtins.object' + +def g() -> None: + x = 1 + if 1: + x, x = '', 1 \ + # E: Incompatible types in assignment (expression has type "str", variable has type "int") + +[case testRedefineUnderscore] +def f() -> None: + _, _ = 1, '' + if 1: + _, _ = '', 1 + reveal_type(_) # E: Revealed type is 'Any' From ab70d0db797f452d5e595c7e77748af2ed211fd9 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 20 Sep 2018 13:26:34 +0100 Subject: [PATCH 06/47] Fix redefinition with 'break' and 'continue' --- mypy/semanal.py | 6 +++++ mypy/semanal_shared.py | 17 ++++++++++++ test-data/unit/check-redefine.test | 43 ++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/mypy/semanal.py b/mypy/semanal.py index 57b52bc53297..4036e14cefdc 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2632,7 +2632,9 @@ def visit_operator_assignment_stmt(self, def visit_while_stmt(self, s: WhileStmt) -> None: s.expr.accept(self) self.loop_depth += 1 + self.var_def_analyzer.enter_loop() s.body.accept(self) + self.var_def_analyzer.leave_loop() self.loop_depth -= 1 self.visit_block_maybe(s.else_body) @@ -2649,16 +2651,20 @@ def visit_for_stmt(self, s: ForStmt) -> None: self.store_declared_types(s.index, s.index_type) self.loop_depth += 1 + self.var_def_analyzer.enter_loop() self.visit_block(s.body) + self.var_def_analyzer.leave_loop() self.loop_depth -= 1 self.visit_block_maybe(s.else_body) def visit_break_stmt(self, s: BreakStmt) -> None: + self.var_def_analyzer.reject_redefinition_of_vars_in_loop() if self.loop_depth == 0: self.fail("'break' outside loop", s, True, blocker=True) def visit_continue_stmt(self, s: ContinueStmt) -> None: + self.var_def_analyzer.reject_redefinition_of_vars_in_loop() if self.loop_depth == 0: self.fail("'continue' outside loop", s, True, blocker=True) diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index 42c43844c4a5..9dc8ffcdebe5 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -167,7 +167,11 @@ class VarDefAnalyzer: def __init__(self) -> None: self.block_id = 0 self.disallow_redef_depth = 0 + self.loop_depth = 0 + # Map block id to loop depth. + self.block_loop_depth = {} # type: Dict[int, int] self.blocks = [] # type: List[int] + # List of scopes; each scope maps short name to block id. self.var_blocks = [{}] # type: List[Dict[str, int]] def clear(self) -> None: @@ -177,6 +181,7 @@ def clear(self) -> None: def enter_block(self) -> None: self.block_id += 1 self.blocks.append(self.block_id) + self.block_loop_depth[self.block_id] = self.loop_depth def leave_block(self) -> None: self.blocks.pop() @@ -187,6 +192,12 @@ def enter_with_or_try(self) -> None: def leave_with_or_try(self) -> None: self.disallow_redef_depth -= 1 + def enter_loop(self) -> None: + self.loop_depth += 1 + + def leave_loop(self) -> None: + self.loop_depth -= 1 + def current_block(self) -> int: return self.blocks[-1] @@ -206,6 +217,12 @@ def reject_redefinition_of_vars_in_scope(self) -> None: for key in var_blocks: var_blocks[key] = -1 + def reject_redefinition_of_vars_in_loop(self) -> None: + var_blocks = self.var_blocks[-1] + for key, block in var_blocks.items(): + if self.block_loop_depth[block] == self.loop_depth: + var_blocks[key] = -1 + def process_assignment(self, name: str, can_be_redefined: bool) -> bool: """Record assignment to given name and return True if it defines a new name.""" if self.disallow_redef_depth > 0: diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index 301186c9f644..079b39b5cf73 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -169,3 +169,46 @@ def f() -> None: if 1: _, _ = '', 1 reveal_type(_) # E: Revealed type is 'Any' + +[case testRedefineWithBreakAndContinue] +def f() -> None: + y = 0 + while int(): + z = 0 + z = '' + x = 0 + if int(): + break + x = '' \ + # E: Incompatible types in assignment (expression has type "str", variable has type "int") + reveal_type(x) # E: Revealed type is 'builtins.int' + y = '' + +def g() -> None: + y = 0 + for a in h(): + z = 0 + z = '' + x = 0 + if int(): + continue + x = '' \ + # E: Incompatible types in assignment (expression has type "str", variable has type "int") + reveal_type(x) # E: Revealed type is 'builtins.int' + y = '' + +def h(): pass + +[case testRedefineLocalAndNestedLoops] +def f() -> None: + z = 0 + while int(): + x = 0 + while int(): + if 1: + y = 1 + if int(): + break + y = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") + x = '' + z = '' From 525d5f480a06425a5a0c7528b5a8d71cfa89505a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 20 Sep 2018 14:10:51 +0100 Subject: [PATCH 07/47] Fix issues --- mypy/semanal_shared.py | 2 +- test-data/unit/check-functions.test | 38 +++++++++++++++++------------ 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index 9dc8ffcdebe5..482be82c90cc 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -220,7 +220,7 @@ def reject_redefinition_of_vars_in_scope(self) -> None: def reject_redefinition_of_vars_in_loop(self) -> None: var_blocks = self.var_blocks[-1] for key, block in var_blocks.items(): - if self.block_loop_depth[block] == self.loop_depth: + if self.block_loop_depth.get(block) == self.loop_depth: var_blocks[key] = -1 def process_assignment(self, name: str, can_be_redefined: bool) -> bool: diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 6ab359241ecd..242117b6fe61 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -1197,8 +1197,9 @@ from typing import Any x = None # type: Any if x: def f(x: int) -> None: - x = 1 - x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") + if 1: + x = 1 + x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") [out] [case testCallConditionalFunction] @@ -1216,18 +1217,20 @@ from typing import Any x = None # type: Any if x: def f(x: int) -> None: - x = 'x' # fail - x = 1 + 'x' + x # fail + if 1: + x = 1 else: def f(x: int) -> None: x + 'x' # fail - x = 1 + if 1: + x = 1 f(1) f('x') # fail [out] -main:5: error: Incompatible types in assignment (expression has type "str", variable has type "int") -main:9: error: Unsupported operand types for + ("int" and "str") -main:12: error: Argument 1 to "f" has incompatible type "str"; expected "int" +main:5: error: Unsupported operand types for + ("str" and "int") +main:10: error: Unsupported operand types for + ("int" and "str") +main:14: error: Argument 1 to "f" has incompatible type "str"; expected "int" [case testNestedConditionalFunctionDefinitionWithIfElse] from typing import Any @@ -1441,8 +1444,9 @@ x = None # type: Any class A: if x: def f(self, x: int) -> None: - x = 1 - x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") + if 1: + x = 1 + x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") [out] [case testCallConditionalMethodInClassBody] @@ -1472,18 +1476,20 @@ x = None # type: Any class A: if x: def f(self, x: int) -> None: - x = 'x' # fail - x = 1 + 'x' + x # fail + if 1: + x = 1 else: def f(self, x: int) -> None: x + 'x' # fail - x = 1 + if 1: + x = 1 A().f(1) A().f('x') # fail [out] -main:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") -main:10: error: Unsupported operand types for + ("int" and "str") -main:13: error: Argument 1 to "f" of "A" has incompatible type "str"; expected "int" +main:6: error: Unsupported operand types for + ("str" and "int") +main:11: error: Unsupported operand types for + ("int" and "str") +main:15: error: Argument 1 to "f" of "A" has incompatible type "str"; expected "int" [case testUnconditionalRedefinitionOfConditionalMethod] from typing import Any From 2c614a208198b36a07c4f815de7171f93f42f39d Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 20 Sep 2018 16:28:39 +0100 Subject: [PATCH 08/47] Fix some edge cases --- mypy/semanal.py | 14 ++++++++--- mypy/semanal_shared.py | 6 ++++- test-data/unit/check-inference.test | 6 ++--- test-data/unit/check-redefine.test | 36 +++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 7 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 4036e14cefdc..b09eb2ad1eba 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -387,8 +387,8 @@ def visit_func_def(self, defn: FuncDef) -> None: def _visit_func_def(self, defn: FuncDef) -> None: phase_info = self.postpone_nested_functions_stack[-1] if phase_info != FUNCTION_SECOND_PHASE: - self.function_stack.append(defn) # First phase of analysis for function. + self.function_stack.append(defn) if not defn._fullname: defn._fullname = self.qualified_name(defn.name()) if defn.type: @@ -3535,12 +3535,14 @@ def is_module_scope(self) -> bool: def add_symbol(self, name: str, node: SymbolTableNode, context: Context) -> None: + """Add symbol to the currently active symbol table.""" # NOTE: This logic mostly parallels SemanticAnalyzerPass1.add_symbol. If you change # this, you may have to change the other method as well. # TODO: Combine these methods in the first and second pass into a single one. if self.is_func_scope(): assert self.locals[-1] is not None - is_new = self.var_def_analyzer.process_assignment(name, can_be_redefined=False) + is_new = (isinstance(node.node, Var) + and self.var_def_analyzer.process_assignment(name, can_be_redefined=False)) if name in self.locals[-1] and not is_new: # Flag redefinition unless this is a reimport of a module. if not (node.kind == MODULE_REF and @@ -3573,9 +3575,15 @@ def add_symbol(self, name: str, node: SymbolTableNode, def add_local(self, node: Union[Var, FuncDef, OverloadedFuncDef], ctx: Context, allow_redefine: bool = False) -> None: + """Add local variable or function. + + Args: + allow_redefine: If true, override any existing local symbol with the same name + """ assert self.locals[-1] is not None, "Should not add locals outside a function" name = node.name() - if name in self.locals[-1] and not allow_redefine: + if name in self.locals[-1] and (not allow_redefine or + not isinstance(self.locals[-1][name].node, Var)): self.name_already_defined(name, ctx, self.locals[-1][name]) return node._fullname = name diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index 482be82c90cc..f917a18bb1cb 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -224,7 +224,11 @@ def reject_redefinition_of_vars_in_loop(self) -> None: var_blocks[key] = -1 def process_assignment(self, name: str, can_be_redefined: bool) -> bool: - """Record assignment to given name and return True if it defines a new name.""" + """Record assignment to given name and return True if it defines a new name. + + Args: + can_be_redefined: If True, allows assignment in the same block to redefine the name + """ if self.disallow_redef_depth > 0: can_be_redefined = False block = self.current_block() diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index a3453f179158..4ae034e4f773 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -2423,12 +2423,12 @@ _ = 0 # E: Incompatible types in assignment (expression has type "int", variabl def foo() -> None: import _ _.f() - _ = 0 # E: Incompatible types in assignment (expression has type "int", variable has type Module) + _ = 0 # E: Name '_' already defined (by an import) [file b.py] def foo() -> None: import m as _ _.f() - _ = 0 # E: Incompatible types in assignment (expression has type "int", variable has type Module) + _ = 0 # E: Name '_' already defined (by an import) [file c.py] def foo() -> None: from m import _ @@ -2438,7 +2438,7 @@ def foo() -> None: def foo() -> None: from m import f as _ _() - _ = 0 # E: Incompatible types in assignment (expression has type "int", variable has type "Callable[[], Any]") + _ = 0 # E: Name '_' already defined on line 1 [builtins fixtures/module.pyi] [case testUnusedTargetNotClass] diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index 079b39b5cf73..27f05f5ecec1 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -212,3 +212,39 @@ def f() -> None: y = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") x = '' z = '' + +[case testCannotRedefineVarAsFunction] +def f() -> None: + def x(): pass + x = 1 # E: Name 'x' already defined on line 2 + reveal_type(x) # E: Revealed type is 'def () -> Any' + y = 1 + def y(): pass # E: Name 'y' already defined on line 5 + +[case testCannotRedefineVarAsClass] +def f() -> None: + class x: pass + x = 1 # E: Name 'x' already defined (possibly by an import) + y = 1 + class y: pass # E: Name 'y' already defined on line 4 + +[case testCannotRedefineVarAsTypeVar] +from typing import TypeVar +def f() -> None: + x = TypeVar('x') + x = 1 # E: Name 'x' already defined on line 3 + def g(a: x) -> x: pass + reveal_type(g(1)) # E: Revealed type is 'builtins.int*' + y = 1 + # This is arguably inconsistent -- we accept this since it looks like an assignment. + y = TypeVar('y') + def h(a: y) -> y: return a + reveal_type(h('')) # E: Revealed type is 'builtins.str*' + +[case testCannotRedefineVarAsModule] +def f() -> None: + import typing as m + m = 1 # E: Name 'm' already defined (by an import) + n = 1 + import typing as n # E: Name 'n' already defined on line 4 +[builtins fixtures/module.pyi] From bf2f8693e4851674d288f4ce95ebf6215f204fba Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 28 Sep 2018 12:35:24 +0100 Subject: [PATCH 09/47] Support top-level redefinition through renaming (WIP) --- mypy/messages.py | 5 +- mypy/plugins/attrs.py | 7 +- mypy/semanal.py | 183 +++--- mypy/semanal_pass1.py | 18 +- mypy/semanal_redef.py | 185 ++++++ mypy/semanal_shared.py | 23 +- mypy/util.py | 5 + test-data/unit/check-abstract.test | 64 +- test-data/unit/check-basic.test | 45 +- test-data/unit/check-bound.test | 17 +- test-data/unit/check-class-namedtuple.test | 12 +- test-data/unit/check-classes.test | 98 +-- test-data/unit/check-columns.test | 7 +- test-data/unit/check-dynamic-typing.test | 284 +++++---- test-data/unit/check-enum.test | 44 +- test-data/unit/check-expressions.test | 627 +++++++++++--------- test-data/unit/check-final.test | 15 +- test-data/unit/check-flags.test | 6 +- test-data/unit/check-functions.test | 264 ++++++--- test-data/unit/check-generic-subtyping.test | 133 +++-- test-data/unit/check-generics.test | 185 +++--- test-data/unit/check-ignore.test | 6 +- test-data/unit/check-incremental.test | 5 +- test-data/unit/check-inference-context.test | 201 ++++--- test-data/unit/check-inference.test | 246 +++++--- test-data/unit/check-isinstance.test | 26 +- test-data/unit/check-lists.test | 21 +- test-data/unit/check-modules.test | 35 +- test-data/unit/check-namedtuple.test | 65 +- test-data/unit/check-newtype.test | 14 +- test-data/unit/check-optional.test | 6 +- test-data/unit/check-overloading.test | 93 +-- test-data/unit/check-protocols.test | 93 +-- test-data/unit/check-python2.test | 18 +- test-data/unit/check-redefine.test | 118 +++- test-data/unit/check-serialize.test | 10 +- test-data/unit/check-statements.test | 84 +-- test-data/unit/check-tuples.test | 397 ++++++++----- test-data/unit/check-type-aliases.test | 17 +- test-data/unit/check-type-checks.test | 11 +- test-data/unit/check-typeddict.test | 9 +- test-data/unit/check-typevar-values.test | 27 +- test-data/unit/check-unions.test | 30 +- test-data/unit/check-unreachable-code.test | 5 +- test-data/unit/check-varargs.test | 180 +++--- test-data/unit/check-warnings.test | 9 +- test-data/unit/deps.test | 4 +- test-data/unit/fine-grained.test | 31 +- test-data/unit/semanal-statements.test | 57 ++ test-data/unit/typexport-basic.test | 21 +- 50 files changed, 2585 insertions(+), 1481 deletions(-) create mode 100644 mypy/semanal_redef.py diff --git a/mypy/messages.py b/mypy/messages.py index 5800c4fc20a5..921789b18da7 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -29,6 +29,7 @@ ReturnStmt, NameExpr, Var, CONTRAVARIANT, COVARIANT, SymbolNode, CallExpr ) +from mypy.util import unmangle MYPY = False if MYPY: @@ -978,7 +979,7 @@ def cant_assign_to_final(self, name: str, attr_assign: bool, ctx: Context) -> No Pass `attr_assign=True` if the assignment assigns to an attribute. """ kind = "attribute" if attr_assign else "name" - self.fail('Cannot assign to final {} "{}"'.format(kind, name), ctx) + self.fail('Cannot assign to final {} "{}"'.format(kind, unmangle(name)), ctx) def protocol_members_cant_be_final(self, ctx: Context) -> None: self.fail("Protocol member cannot be final", ctx) @@ -1099,7 +1100,7 @@ def unimported_type_becomes_any(self, prefix: str, typ: Type, ctx: Context) -> N ctx) def need_annotation_for_var(self, node: SymbolNode, context: Context) -> None: - self.fail("Need type annotation for '{}'".format(node.name()), context) + self.fail("Need type annotation for '{}'".format(unmangle(node.name())), context) def explicit_any(self, ctx: Context) -> None: self.fail('Explicit "Any" is not allowed', ctx) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 2aa25b59da0e..64748c89c4be 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -19,6 +19,7 @@ Overloaded, Instance, UnionType, FunctionLike ) from mypy.typevars import fill_typevars +from mypy.util import unmangle MYPY = False if MYPY: @@ -337,9 +338,10 @@ def _attribute_from_auto_attrib(ctx: 'mypy.plugin.ClassDefContext', rvalue: Expression, stmt: AssignmentStmt) -> Attribute: """Return an Attribute for a new type assignment.""" + name = unmangle(lhs.name) # `x: int` (without equal sign) assigns rvalue to TempNode(AnyType()) has_rhs = not isinstance(rvalue, TempNode) - return Attribute(lhs.name, ctx.cls.info, has_rhs, True, Converter(), stmt) + return Attribute(name, ctx.cls.info, has_rhs, True, Converter(), stmt) def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext', @@ -396,7 +398,8 @@ def _attribute_from_attrib_maker(ctx: 'mypy.plugin.ClassDefContext', converter = convert converter_info = _parse_converter(ctx, converter) - return Attribute(lhs.name, ctx.cls.info, attr_has_default, init, converter_info, stmt) + name = unmangle(lhs.name) + return Attribute(name, ctx.cls.info, attr_has_default, init, converter_info, stmt) def _parse_converter(ctx: 'mypy.plugin.ClassDefContext', diff --git a/mypy/semanal.py b/mypy/semanal.py index b09eb2ad1eba..57c3c889403a 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -81,7 +81,7 @@ from mypy.options import Options from mypy import experiments from mypy.plugin import Plugin, ClassDefContext, SemanticAnalyzerPluginInterface -from mypy.util import get_prefix, correct_relative_import +from mypy.util import get_prefix, correct_relative_import, unmangle from mypy.semanal_shared import SemanticAnalyzerInterface, set_callable_name, VarDefAnalyzer from mypy.scope import Scope from mypy.semanal_namedtuple import NamedTupleAnalyzer, NAMEDTUPLE_PROHIBITED_NAMES @@ -255,7 +255,6 @@ def __init__(self, # for processing module top levels in fine-grained incremental mode. self.recurse_into_functions = True self.scope = Scope() - self.var_def_analyzer = VarDefAnalyzer() # mypyc doesn't properly handle implementing an abstractproperty # with a regular attribute so we make it a property @@ -284,7 +283,6 @@ def visit_file(self, file_node: MypyFile, fnam: str, options: Options, self.typed_dict_analyzer = TypedDictAnalyzer(options, self, self.msg) self.enum_call_analyzer = EnumCallAnalyzer(options, self) self.newtype_analyzer = NewTypeAnalyzer(options, self, self.msg) - self.var_def_analyzer.clear() with experiments.strict_optional_set(options.strict_optional): if 'builtins' in self.modules: @@ -300,10 +298,8 @@ def visit_file(self, file_node: MypyFile, fnam: str, options: Options, defs = file_node.defs self.scope.enter_file(file_node.fullname()) - self.var_def_analyzer.enter_block() for d in defs: self.accept(d) - self.var_def_analyzer.leave_block() self.scope.leave() if self.cur_mod_id == 'builtins': @@ -325,13 +321,11 @@ def refresh_partial(self, node: Union[MypyFile, FuncItem, OverloadedFuncDef], patches: List[Tuple[int, Callable[[], None]]]) -> None: """Refresh a stale target in fine-grained incremental mode.""" self.patches = patches - self.var_def_analyzer.enter_block() if isinstance(node, MypyFile): self.refresh_top_level(node) else: self.recurse_into_functions = True self.accept(node) - self.var_def_analyzer.leave_block() del self.patches def refresh_top_level(self, file_node: MypyFile) -> None: @@ -374,16 +368,9 @@ def visit_func_def(self, defn: FuncDef) -> None: if not self.recurse_into_functions: return - # Conservatively do not allow variable defined before a function to - # be redefined later, since function could refer to either definition. - self.var_def_analyzer.reject_redefinition_of_vars_in_scope() - self.var_def_analyzer.enter_scope() - with self.scope.function_scope(defn): self._visit_func_def(defn) - self.var_def_analyzer.leave_scope() - def _visit_func_def(self, defn: FuncDef) -> None: phase_info = self.postpone_nested_functions_stack[-1] if phase_info != FUNCTION_SECOND_PHASE: @@ -718,7 +705,7 @@ def analyze_function(self, defn: FuncItem) -> None: self.function_stack.append(defn) self.enter() for arg in defn.arguments: - self.var_def_analyzer.process_assignment(arg.variable.name(), True) + #self.var_def_analyzer.process_assignment(arg.variable.name(), True) self.add_local(arg.variable, defn) # The first argument of a non-static, non-class method is like 'self' @@ -1653,13 +1640,13 @@ def visit_block(self, b: Block, new_var_scope: bool = True) -> None: if b.is_unreachable: return self.block_depth[-1] += 1 - if new_var_scope: - self.var_def_analyzer.enter_block() + #if new_var_scope: + # self.var_def_analyzer.enter_block() for s in b.body: self.accept(s) self.block_depth[-1] -= 1 - if new_var_scope: - self.var_def_analyzer.leave_block() + #if new_var_scope: + # self.var_def_analyzer.leave_block() def visit_block_maybe(self, b: Optional[Block]) -> None: if b: @@ -1714,18 +1701,15 @@ def add_type_alias_deps(self, aliases_used: Iterable[str], self.cur_mod_node.alias_deps[target].update(aliases_used) def visit_assignment_stmt(self, s: AssignmentStmt) -> None: - self.unwrap_final(s) - s.rvalue.accept(self) + is_final = self.unwrap_final(s) + s.is_final_def = is_final - def final_cb(keep_final: bool) -> None: - self.fail("Cannot redefine an existing name as final", s) - if not keep_final: - s.is_final_def = False + s.rvalue.accept(self) has_initializer = not isinstance(s.rvalue, TempNode) for lval in s.lvalues: self.analyze_lvalue(lval, explicit_type=s.type is not None, - final_cb=final_cb if s.is_final_def else None, + is_final=s.is_final_def, has_initializer=has_initializer) self.check_final_implicit_def(s) self.check_classvar(s) @@ -1763,15 +1747,19 @@ def final_cb(keep_final: bool) -> None: isinstance(s.rvalue, (ListExpr, TupleExpr))): self.add_exports(s.rvalue.items) - def unwrap_final(self, s: AssignmentStmt) -> None: + def unwrap_final(self, s: AssignmentStmt) -> bool: """Strip Final[...] if present in an assignment. This is done to invoke type inference during type checking phase for this - assignment. Also, Final[...] desn't affect type in any way, it is rather an + assignment. Also, Final[...] desn't affect type in any way -- it is rather an access qualifier for given `Var`. + + Also perform various consistency checks. + + Returns True if Final[...] was present. """ if not s.type or not self.is_final_type(s.type): - return + return False assert isinstance(s.type, UnboundType) if len(s.type.args) > 1: self.fail("Final[...] takes at most one type argument", s.type) @@ -1785,10 +1773,9 @@ def unwrap_final(self, s: AssignmentStmt) -> None: s.type = s.type.args[0] if len(s.lvalues) != 1 or not isinstance(s.lvalues[0], RefExpr): self.fail("Invalid final declaration", s) - return + return False lval = s.lvalues[0] assert isinstance(lval, RefExpr) - s.is_final_def = True if self.loop_depth > 0: self.fail("Cannot use Final inside a loop", s) if self.type and self.type.is_protocol: @@ -1797,7 +1784,7 @@ def unwrap_final(self, s: AssignmentStmt) -> None: not self.is_stub_file and not self.is_class_scope()): if not invalid_bare_final: # Skip extra error messages. self.msg.final_without_value(s) - return + return True def check_final_implicit_def(self, s: AssignmentStmt) -> None: """Do basic checks for final declaration on self in __init__. @@ -1996,24 +1983,24 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> None: def analyze_lvalue(self, lval: Lvalue, nested: bool = False, add_global: bool = False, explicit_type: bool = False, - final_cb: Optional[Callable[[bool], None]] = None, + is_final: bool = False, has_initializer: bool = True) -> None: """Analyze an lvalue or assignment target. + Note that this is used in both pass 1 and 2. + Args: lval: The target lvalue nested: If true, the lvalue is within a tuple or list lvalue expression add_global: Add name to globals table only if this is true (used in first pass) explicit_type: Assignment has type annotation - final_cb: A callback to call in situation where a final declaration on `self` - overrides an existing name. has_initializer: Is there a rvalue (i.e. not just "x: t")? """ if isinstance(lval, NameExpr): - self.analyze_name_lvalue(lval, add_global, explicit_type, final_cb, has_initializer) + self.analyze_name_lvalue(lval, add_global, explicit_type, is_final, has_initializer) elif isinstance(lval, MemberExpr): if not add_global: - self.analyze_member_lvalue(lval, explicit_type, final_cb=final_cb) + self.analyze_member_lvalue(lval, explicit_type, is_final) if explicit_type and not self.is_self_member_ref(lval): self.fail('Type cannot be declared in assignment to non-self ' 'attribute', lval) @@ -2039,13 +2026,20 @@ def analyze_name_lvalue(self, lval: NameExpr, add_global: bool, explicit_type: bool, - final_cb: Optional[Callable[[bool], None]], + is_final: bool, has_initializer: bool) -> None: """Analyze an lvalue that targets a name expression. Arguments are similar to "analyze_lvalue". """ - is_new = self.var_def_analyzer.process_assignment(lval.name, has_initializer) + if self.is_alias_for_final_name(lval.name): + if is_final: + self.fail("Cannot redefine an existing name as final", lval) + else: + self.msg.cant_assign_to_final(lval.name, self.type is not None, lval) + + #is_new = self.var_def_analyzer.process_assignment(lval.name, True, not has_initializer) + # Top-level definitions within some statements (at least while) are # not handled in the first pass, so they have to be added now. nested_global = (not self.is_func_scope() and @@ -2060,30 +2054,74 @@ def analyze_name_lvalue(self, # Since the is_new_def flag is set, this must have been analyzed # already in the first pass and added to the symbol table. # An exception is typing module with incomplete test fixtures. + if (is_final + and unmangle(lval.name) + "'" in self.globals + and unmangle(lval.name) + "'" != lval.name): + self.fail("Cannot redefine an existing name as final", lval) assert lval.node.name() in self.globals or self.cur_mod_id == 'typing' elif (self.locals[-1] is not None - and (lval.name not in self.locals[-1] - or (is_new and not self.is_local_final(lval.name))) + and (lval.name not in self.locals[-1]) + # or (is_new and not self.is_local_final(lval.name))) and lval.name not in self.global_decls[-1] and lval.name not in self.nonlocal_decls[-1]): # Define new local name. v = self.make_name_lvalue_var(lval, LDEF) - self.add_local(v, lval, allow_redefine=is_new) + self.add_local(v, lval) #, allow_redefine=is_new) if lval.name == '_': + 1 / 0 # Special case for assignment to local named '_': always infer 'Any'. typ = AnyType(TypeOfAny.special_form) self.store_declared_types(lval, typ) elif not self.is_func_scope() and (self.type and lval.name not in self.type.names): # Define a new attribute within class body. + if is_final and unmangle(lval.name) + "'" in self.type.names: + self.fail("Cannot redefine an existing name as final", lval) v = self.make_name_lvalue_var(lval, MDEF) v.is_inferred = not explicit_type self.type.names[lval.name] = SymbolTableNode(MDEF, v) else: - self.make_name_lvalue_point_to_existing_def(lval, explicit_type, final_cb) + self.make_name_lvalue_point_to_existing_def(lval, explicit_type, is_final) - def is_local_final(self, name: str) -> bool: - return is_final_node(self.locals[-1][name].node) + def get_original_def(self, name: str) -> Optional[SymbolTableNode]: + if self.is_func_scope(): + if not name.endswith("'"): + # Not a mangled name -- can't be an alias + return False + name = unmangle(name) + assert self.locals[-1] is not None, "No locals at function scope" + return self.locals[-1].get(name) + elif self.type is not None: + # TODO + return None + else: + orig_name = unmangle(name) + "'" + if name == orig_name: + return None + return self.globals.get(orig_name) + + def is_alias_for_final_name(self, name: str) -> bool: + if self.is_func_scope(): + if not name.endswith("'"): + # Not a mangled name -- can't be an alias + return False + name = unmangle(name) + assert self.locals[-1] is not None, "No locals at function scope" + existing = self.locals[-1].get(name) + return existing is not None and is_final_node(existing.node) + elif self.type is not None: + orig_name = unmangle(name) + "'" + if name == orig_name: + return False + existing = self.type.names.get(orig_name) + return existing is not None and is_final_node(existing.node) + else: + orig_name = unmangle(name) + "'" + if name == orig_name: + return False + existing = self.globals.get(orig_name) + return existing is not None and is_final_node(existing.node) + #return is_final_node(self.locals[-1][name].node) def make_name_lvalue_var(self, lvalue: NameExpr, kind: int) -> Var: """Return a Var node for an lvalue that is a name expression.""" @@ -2111,7 +2149,11 @@ def make_name_lvalue_point_to_existing_def( self, lval: NameExpr, explicit_type: bool, - final_cb: Optional[Callable[[bool], None]]) -> None: + is_final: bool) -> None: + """Update an lvalue to point to existing definition in the same scope. + + Arguments are similar to "analyze_lvalue". + """ # Assume that an existing name exists. Try to find the original definition. global_def = self.globals.get(lval.name) if self.locals: @@ -2127,12 +2169,8 @@ def make_name_lvalue_point_to_existing_def( original_def = global_def or local_def or type_def # Redefining an existing name with final is always an error. - if final_cb is not None: - # We avoid extra errors if the original definition is also final - # by keeping the final status of this assignment. - keep_final = bool(original_def and isinstance(original_def.node, Var) and - original_def.node.is_final) - final_cb(keep_final) + if is_final: + self.fail("Cannot redefine an existing name as final", lval) if explicit_type: # Don't re-bind types self.name_already_defined(lval.name, lval, original_def) @@ -2157,30 +2195,28 @@ def analyze_tuple_or_list_lvalue(self, lval: TupleExpr, self.analyze_lvalue(i, nested=True, add_global=add_global, explicit_type=explicit_type) - def analyze_member_lvalue(self, lval: MemberExpr, explicit_type: bool = False, - final_cb: Optional[Callable[[bool], None]] = None) -> None: + def analyze_member_lvalue(self, lval: MemberExpr, explicit_type: bool, is_final: bool) -> None: """Analyze lvalue that is a member expression. Arguments: lval: The target lvalue explicit_type: Assignment has type annotation - final_cb: A callback to call in situation where a final declaration on `self` - overrides an existing name. + is_final: Is the target final """ lval.accept(self) if self.is_self_member_ref(lval): assert self.type, "Self member outside a class" cur_node = self.type.names.get(lval.name, None) node = self.type.get(lval.name) - if cur_node and final_cb is not None: + if cur_node and is_final: # Overrides will be checked in type checker. - final_cb(False) + self.fail("Cannot redefine an existing name as final", lval) # If the attribute of self is not defined in superclasses, create a new Var, ... if ((node is None or isinstance(node.node, Var) and node.node.is_abstract_var) or # ... also an explicit declaration on self also creates a new Var. # Note that `explicit_type` might has been erased for bare `Final`, # so we also check if `final_cb` is passed. - (cur_node is None and (explicit_type or final_cb is not None))): + (cur_node is None and (explicit_type or is_final))): if self.type.is_protocol and node is None: self.fail("Protocol members cannot be defined via assignment to self", lval) else: @@ -2305,6 +2341,7 @@ def process_typevar_declaration(self, s: AssignmentStmt) -> None: node.node = TypeVar def check_typevar_name(self, call: CallExpr, name: str, context: Context) -> bool: + name = unmangle(name) if len(call.args) < 1: self.fail("Too few arguments for TypeVar()", context) return False @@ -2632,9 +2669,9 @@ def visit_operator_assignment_stmt(self, def visit_while_stmt(self, s: WhileStmt) -> None: s.expr.accept(self) self.loop_depth += 1 - self.var_def_analyzer.enter_loop() + #self.var_def_analyzer.enter_loop() s.body.accept(self) - self.var_def_analyzer.leave_loop() + #self.var_def_analyzer.leave_loop() self.loop_depth -= 1 self.visit_block_maybe(s.else_body) @@ -2651,20 +2688,20 @@ def visit_for_stmt(self, s: ForStmt) -> None: self.store_declared_types(s.index, s.index_type) self.loop_depth += 1 - self.var_def_analyzer.enter_loop() + #self.var_def_analyzer.enter_loop() self.visit_block(s.body) - self.var_def_analyzer.leave_loop() + #self.var_def_analyzer.leave_loop() self.loop_depth -= 1 self.visit_block_maybe(s.else_body) def visit_break_stmt(self, s: BreakStmt) -> None: - self.var_def_analyzer.reject_redefinition_of_vars_in_loop() + #self.var_def_analyzer.reject_redefinition_of_vars_in_loop() if self.loop_depth == 0: self.fail("'break' outside loop", s, True, blocker=True) def visit_continue_stmt(self, s: ContinueStmt) -> None: - self.var_def_analyzer.reject_redefinition_of_vars_in_loop() + #self.var_def_analyzer.reject_redefinition_of_vars_in_loop() if self.loop_depth == 0: self.fail("'continue' outside loop", s, True, blocker=True) @@ -2680,9 +2717,9 @@ def visit_try_stmt(self, s: TryStmt) -> None: def analyze_try_stmt(self, s: TryStmt, visitor: NodeVisitor[None], add_global: bool = False) -> None: - self.var_def_analyzer.enter_with_or_try() + #self.var_def_analyzer.enter_with_or_try() s.body.accept(visitor) - self.var_def_analyzer.leave_with_or_try() + #self.var_def_analyzer.leave_with_or_try() for type, var, handler in zip(s.types, s.vars, s.handlers): if type: type.accept(visitor) @@ -2739,9 +2776,9 @@ def visit_with_stmt(self, s: WithStmt) -> None: elif isinstance(s.target_type, TupleType): s.target_type = s.target_type.copy_modified(items=new_types) - self.var_def_analyzer.enter_with_or_try() + #self.var_def_analyzer.enter_with_or_try() self.visit_block(s.body) - self.var_def_analyzer.leave_with_or_try() + #self.var_def_analyzer.leave_with_or_try() def visit_del_stmt(self, s: DelStmt) -> None: s.expr.accept(self) @@ -3541,9 +3578,9 @@ def add_symbol(self, name: str, node: SymbolTableNode, # TODO: Combine these methods in the first and second pass into a single one. if self.is_func_scope(): assert self.locals[-1] is not None - is_new = (isinstance(node.node, Var) - and self.var_def_analyzer.process_assignment(name, can_be_redefined=False)) - if name in self.locals[-1] and not is_new: + #is_new = (isinstance(node.node, Var) + # and self.var_def_analyzer.process_assignment(name, can_be_redefined=False)) + if name in self.locals[-1]: # and not is_new: # Flag redefinition unless this is a reimport of a module. if not (node.kind == MODULE_REF and self.locals[-1][name].node == node.node): @@ -3635,7 +3672,7 @@ def name_already_defined(self, name: str, ctx: Context, extra_msg = ' on line {}'.format(node.line) else: extra_msg = ' (possibly by an import)' - self.fail("Name '{}' already defined{}".format(name, extra_msg), ctx) + self.fail("Name '{}' already defined{}".format(unmangle(name), extra_msg), ctx) def fail(self, msg: str, ctx: Context, serious: bool = False, *, blocker: bool = False) -> None: diff --git a/mypy/semanal_pass1.py b/mypy/semanal_pass1.py index bf1747a9185d..70e18d715ee3 100644 --- a/mypy/semanal_pass1.py +++ b/mypy/semanal_pass1.py @@ -14,7 +14,8 @@ bind names, which only happens in pass 2. This pass also infers the reachability of certain if statements, such as -those with platform checks. +those with platform checks. This lets us filter out unreachable imports +at an early stage. """ from typing import List, Tuple @@ -23,8 +24,8 @@ from mypy.nodes import ( MypyFile, SymbolTable, SymbolTableNode, Var, Block, AssignmentStmt, FuncDef, Decorator, ClassDef, TypeInfo, ImportFrom, Import, ImportAll, IfStmt, WhileStmt, ForStmt, WithStmt, - TryStmt, OverloadedFuncDef, Lvalue, Context, ImportedName, LDEF, GDEF, MDEF, UNBOUND_IMPORTED, - MODULE_REF, implicit_module_attrs + TryStmt, OverloadedFuncDef, Lvalue, Context, ImportedName, TempNode, LDEF, GDEF, MDEF, + UNBOUND_IMPORTED, MODULE_REF, implicit_module_attrs ) from mypy.types import Type, UnboundType, UnionType, AnyType, TypeOfAny, NoneTyp, CallableType from mypy.semanal import SemanticAnalyzerPass2, infer_reachability_of_if_statement @@ -32,6 +33,7 @@ from mypy.options import Options from mypy.sametypes import is_same_type from mypy.visitor import NodeVisitor +from mypy.semanal_redef import VariableRenameVisitor class SemanticAnalyzerPass1(NodeVisitor[None]): @@ -59,6 +61,8 @@ def visit_file(self, file: MypyFile, fnam: str, mod_id: str, options: Options) - and these will get resolved in later phases of semantic analysis. """ + # Perform renaming across the AST + file.accept(VariableRenameVisitor()) sem = self.sem self.sem.options = options # Needed because we sometimes call into it self.pyversion = options.python_version @@ -92,10 +96,10 @@ def visit_file(self, file: MypyFile, fnam: str, mod_id: str, options: Options) - v._fullname = self.sem.qualified_name(name) self.sem.globals[name] = SymbolTableNode(GDEF, v) - self.sem.var_def_analyzer.enter_block() + #self.sem.var_def_analyzer.enter_block() for d in defs: d.accept(self) - self.sem.var_def_analyzer.leave_block() + #self.sem.var_def_analyzer.leave_block() # Add implicit definition of literals/keywords to builtins, as we # cannot define a variable with them explicitly. @@ -140,10 +144,10 @@ def visit_block(self, b: Block) -> None: if b.is_unreachable: return self.sem.block_depth[-1] += 1 - self.sem.var_def_analyzer.enter_block() + #self.sem.var_def_analyzer.enter_block() for node in b.body: node.accept(self) - self.sem.var_def_analyzer.leave_block() + #self.sem.var_def_analyzer.leave_block() self.sem.block_depth[-1] -= 1 def visit_assignment_stmt(self, s: AssignmentStmt) -> None: diff --git a/mypy/semanal_redef.py b/mypy/semanal_redef.py new file mode 100644 index 000000000000..1b17b51a41cb --- /dev/null +++ b/mypy/semanal_redef.py @@ -0,0 +1,185 @@ +from typing import Dict, List + +from mypy.nodes import ( + Block, AssignmentStmt, NameExpr, MypyFile, FuncDef, Lvalue, ListExpr, TupleExpr, TempNode, + WhileStmt, ForStmt, BreakStmt, ContinueStmt, TryStmt, WithStmt, StarExpr, ImportFrom, MemberExpr, + IndexExpr +) +from mypy.traverser import TraverserVisitor +from mypy.semanal_shared import VarDefAnalyzer + + +class VariableRenameVisitor(TraverserVisitor): + """Rename variables to allow redefinition of variables. + + For example, consider this code: + + x = 0 + f(x) + x = '' + g(x) + + It can be renamed to this: + + x~ = 0 + f(x~) + x = '' + g(X) + + TODO: + * renaming in functions (argument redef) + * loops + * break/continue + * break/continue + * inititalizer / no initializer + * nested functions + * prevent redefinition -> no need to rename internally + * Final + + - multiple renamings + * global + - local + + - kinds of names + * for index variables + * imports + - funcdef and such + - classdef + - other ways of assigning to variables + + - nested class + - overloaded func + - decorated func + """ + + def __init__(self) -> None: + self.var_def_analyzer = VarDefAnalyzer() + self.refs = [] # type: List[Dict[str, List[List[NameExpr]]]] + + def visit_mypy_file(self, file_node: MypyFile) -> None: + self.var_def_analyzer.clear() + self.var_def_analyzer.enter_block() + self.refs.append({}) + for d in file_node.defs: + d.accept(self) + self.flush_refs() + self.var_def_analyzer.leave_block() + + def visit_func_def(self, fdef: FuncDef) -> None: + # Conservatively do not allow variable defined before a function to + # be redefined later, since function could refer to either definition. + self.var_def_analyzer.reject_redefinition_of_vars_in_scope() + self.var_def_analyzer.process_assignment(fdef.name(), can_be_redefined=False) + self.var_def_analyzer.enter_scope() + self.refs.append({}) + + for arg in fdef.arguments: + name = arg.variable.name() + self.var_def_analyzer.process_assignment(arg.variable.name(), + can_be_redefined=True) + self.handle_arg(name) + + self.visit_block(fdef.body, enter=False) + self.flush_refs() + self.var_def_analyzer.leave_scope() + + def visit_block(self, block: Block, enter: bool = True) -> None: + if enter: + self.var_def_analyzer.enter_block() + super().visit_block(block) + if enter: + self.var_def_analyzer.leave_block() + + def visit_while_stmt(self, stmt: WhileStmt) -> None: + self.var_def_analyzer.enter_loop() + super().visit_while_stmt(stmt) + self.var_def_analyzer.leave_loop() + + def visit_for_stmt(self, stmt: ForStmt) -> None: + self.analyze_lvalue(stmt.index, True) + self.var_def_analyzer.enter_loop() + super().visit_for_stmt(stmt) + self.var_def_analyzer.leave_loop() + + def visit_break_stmt(self, stmt: BreakStmt) -> None: + self.var_def_analyzer.reject_redefinition_of_vars_in_loop() + + def visit_continue_stmt(self, stmt: ContinueStmt) -> None: + self.var_def_analyzer.reject_redefinition_of_vars_in_loop() + + def visit_try_stmt(self, stmt: TryStmt) -> None: + self.var_def_analyzer.enter_with_or_try() + super().visit_try_stmt(stmt) + self.var_def_analyzer.leave_with_or_try() + + def visit_with_stmt(self, stmt: WithStmt) -> None: + self.var_def_analyzer.enter_with_or_try() + super().visit_with_stmt(stmt) + self.var_def_analyzer.leave_with_or_try() + + def visit_assignment_stmt(self, s: AssignmentStmt) -> None: + has_initializer = not isinstance(s.rvalue, TempNode) + s.rvalue.accept(self) + for lvalue in s.lvalues: + self.analyze_lvalue(lvalue, has_initializer) + + def analyze_lvalue(self, lvalue: Lvalue, has_initializer: bool) -> None: + if isinstance(lvalue, NameExpr): + name = lvalue.name + is_new = self.var_def_analyzer.process_assignment(name, True, not has_initializer) + if is_new: # and name != '_': # Underscore gets special handling later + self.handle_def(lvalue) + else: + self.handle_ref(lvalue) + elif isinstance(lvalue, (ListExpr, TupleExpr)): + for item in lvalue.items: + self.analyze_lvalue(item, has_initializer) + elif isinstance(lvalue, MemberExpr): + lvalue.expr.accept(self) + elif isinstance(lvalue, IndexExpr): + lvalue.base.accept(self) + lvalue.index.accept(self) + elif isinstance(lvalue, StarExpr): + self.analyze_lvalue(lvalue.expr, has_initializer) + + def visit_import_from(self, imp: ImportFrom) -> None: + for id, as_id in imp.names: + self.var_def_analyzer.process_assignment(as_id or id, False, False) + + def visit_name_expr(self, expr: NameExpr) -> None: + self.handle_ref(expr) + + def handle_arg(self, name: str) -> None: + if name not in self.refs[-1]: + self.refs[-1][name] = [[]] + + def handle_def(self, expr: NameExpr) -> None: + names = self.refs[-1].setdefault(expr.name, []) + names.append([expr]) + + def handle_ref(self, expr: NameExpr) -> None: + name = expr.name + if name in self.refs[-1]: + names = self.refs[-1][name] + if not names: + names.append([]) + names[-1].append(expr) + + def flush_refs(self) -> None: + is_func = self.var_def_analyzer.is_nested() + for name, refs in self.refs[-1].items(): + if len(refs) == 1: + continue + if is_func: + to_rename = refs[1:] + else: + to_rename = refs[:-1] + for i, item in enumerate(to_rename): + self.rename_refs(item, i) + self.refs.pop() + + def rename_refs(self, names: List[NameExpr], index: int) -> None: + name = names[0].name + new_name = name + "'" * (index + 1) + for expr in names: + expr.name = new_name diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index f917a18bb1cb..3af38d9bbb3e 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -1,7 +1,7 @@ """Shared definitions used by different parts of semantic analysis.""" from abc import abstractmethod, abstractproperty -from typing import Optional, List, Callable +from typing import Optional, List, Callable, Dict, Set from mypy_extensions import trait from mypy.nodes import ( @@ -161,7 +161,7 @@ class VarDefAnalyzer: x = 0 x = str(x) # Defines a new 'x' - Since we have two distinct 'x' variables, they can have independent inferred types. + Since we now have two distinct 'x' variables, they can have independent inferred types. """ def __init__(self) -> None: @@ -170,9 +170,13 @@ def __init__(self) -> None: self.loop_depth = 0 # Map block id to loop depth. self.block_loop_depth = {} # type: Dict[int, int] + # Stack of block ids being processed. self.blocks = [] # type: List[int] # List of scopes; each scope maps short name to block id. self.var_blocks = [{}] # type: List[Dict[str, int]] + # Variables which have no assigned value yet (e.g., "x: t" but no assigment). + # Assignment in any block is considered an initialization. + self.uninitialized = set() # type: Set[str] def clear(self) -> None: self.blocks = [] @@ -207,6 +211,9 @@ def enter_scope(self) -> None: def leave_scope(self) -> None: self.var_blocks.pop() + def is_nested(self) -> int: + return len(self.var_blocks) > 1 + def reject_redefinition_of_vars_in_scope(self) -> None: """Make it impossible to redefine defined variables in the current scope. @@ -223,16 +230,24 @@ def reject_redefinition_of_vars_in_loop(self) -> None: if self.block_loop_depth.get(block) == self.loop_depth: var_blocks[key] = -1 - def process_assignment(self, name: str, can_be_redefined: bool) -> bool: + def process_assignment(self, name: str, can_be_redefined: bool, no_value: bool = False) -> bool: """Record assignment to given name and return True if it defines a new name. Args: can_be_redefined: If True, allows assignment in the same block to redefine the name + no_value: If True, the first assignment we encounter will not be considered to redefine + this but to initilize it (in any block) """ if self.disallow_redef_depth > 0: can_be_redefined = False block = self.current_block() var_blocks = self.var_blocks[-1] + uninitialized = self.uninitialized + existing_no_value = name in self.uninitialized + if no_value: + uninitialized.add(name) + else: + uninitialized.discard(name) if name not in var_blocks: # New definition if can_be_redefined: @@ -242,7 +257,7 @@ def process_assignment(self, name: str, can_be_redefined: bool) -> bool: # TODO: Make this less restricted. var_blocks[name] = -1 return True - elif var_blocks[name] == block: + elif var_blocks[name] == block and not existing_no_value: # Redefinition return True else: diff --git a/mypy/util.py b/mypy/util.py index bcf5d9edee99..4779366959eb 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -251,3 +251,8 @@ def hard_exit(status: int = 0) -> None: sys.stdout.flush() sys.stderr.flush() os._exit(status) + + +def unmangle(name: str) -> str: + """Remove internal suffixes from a short name.""" + return name.rstrip("'") diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test index 19641b288c0a..b662fc1be473 100644 --- a/test-data/unit/check-abstract.test +++ b/test-data/unit/check-abstract.test @@ -15,6 +15,8 @@ a = None # type: A b = None # type: B c = None # type: C +def f(): i, j, a, b, c # Prevent redefinition + j = c # E: Incompatible types in assignment (expression has type "C", variable has type "J") a = i # E: Incompatible types in assignment (expression has type "I", variable has type "A") a = j # E: Incompatible types in assignment (expression has type "J", variable has type "A") @@ -46,6 +48,8 @@ j = None # type: J a = None # type: A o = None # type: object +def f(): i, j, a, o # Prevent redefinition + j = i # E: Incompatible types in assignment (expression has type "I", variable has type "J") a = i # E: Incompatible types in assignment (expression has type "I", variable has type "A") a = j # E: Incompatible types in assignment (expression has type "J", variable has type "A") @@ -65,18 +69,21 @@ class J(I): pass class A(J): pass [case testInheritingAbstractClassInSubclass] - from abc import abstractmethod, ABCMeta i = None # type: I a = None # type: A b = None # type: B -i = a # E: Incompatible types in assignment (expression has type "A", variable has type "I") -b = a # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + i = a # E: Incompatible types in assignment (expression has type "A", variable has type "I") +if int(): + b = a # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a = b -i = b +if int(): + a = b +if int(): + i = b class I(metaclass=ABCMeta): @abstractmethod @@ -116,12 +123,17 @@ class B: pass i, a, b = None, None, None # type: (I, A, B) o = None # type: object -a = cast(I, o) # E: Incompatible types in assignment (expression has type "I", variable has type "A") -b = cast(B, i) # Ok; a subclass of B might inherit I -i = cast(I, b) # Ok; a subclass of B might inherit I +if int(): + a = cast(I, o) # E: Incompatible types in assignment (expression has type "I", variable has type "A") +if int(): + b = cast(B, i) # Ok; a subclass of B might inherit I +if int(): + i = cast(I, b) # Ok; a subclass of B might inherit I -i = cast(I, o) -i = cast(I, a) +if int(): + i = cast(I, o) +if int(): + i = cast(I, a) [case testInstantiatingClassThatImplementsAbstractMethod] from abc import abstractmethod, ABCMeta @@ -219,14 +231,19 @@ class C(B): var: Type[A] var() var = A # E: Can only assign concrete classes to a variable of type "Type[A]" -var = B # E: Can only assign concrete classes to a variable of type "Type[A]" -var = C # OK +if int(): + var = B # E: Can only assign concrete classes to a variable of type "Type[A]" +if int(): + var = C # OK var_old = None # type: Type[A] # Old syntax for variable annotations var_old() -var_old = A # E: Can only assign concrete classes to a variable of type "Type[A]" -var_old = B # E: Can only assign concrete classes to a variable of type "Type[A]" -var_old = C # OK +if int(): + var_old = A # E: Can only assign concrete classes to a variable of type "Type[A]" +if int(): + var_old = B # E: Can only assign concrete classes to a variable of type "Type[A]" +if int(): + var_old = C # OK [out] [case testInstantiationAbstractsInTypeForClassMethods] @@ -362,7 +379,6 @@ main:11: error: Return type of "h" incompatible with supertype "I" [case testAccessingAbstractMethod] - from abc import abstractmethod, ABCMeta class I(metaclass=ABCMeta): @@ -371,14 +387,16 @@ class I(metaclass=ABCMeta): i, a, b = None, None, None # type: (I, int, str) -a = i.f(a) # E: Incompatible types in assignment (expression has type "str", variable has type "int") -b = i.f(b) # E: Argument 1 to "f" of "I" has incompatible type "str"; expected "int" +if int(): + a = i.f(a) # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + b = i.f(b) # E: Argument 1 to "f" of "I" has incompatible type "str"; expected "int" i.g() # E: "I" has no attribute "g" -b = i.f(a) +if int(): + b = i.f(a) [case testAccessingInheritedAbstractMethod] - from abc import abstractmethod, ABCMeta class J(metaclass=ABCMeta): @@ -388,8 +406,10 @@ class I(J): pass i, a, b = None, None, None # type: (I, int, str) -a = i.f(1) # E: Incompatible types in assignment (expression has type "str", variable has type "int") -b = i.f(1) +if int(): + a = i.f(1) # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + b = i.f(1) -- Any (dynamic) types diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test index 14653cf35b21..56eaff7ed267 100644 --- a/test-data/unit/check-basic.test +++ b/test-data/unit/check-basic.test @@ -2,45 +2,43 @@ [out] [case testAssignmentAndVarDef] - a = None # type: A b = None # type: B -a = a -a = b # Fail +if int(): + a = a +if int(): + a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") class A: pass class B: pass -[out] -main:5: error: Incompatible types in assignment (expression has type "B", variable has type "A") [case testConstructionAndAssignment] - x = None # type: A x = A() -x = B() +if int(): + x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") class A: def __init__(self): pass class B: def __init__(self): pass -[out] -main:4: error: Incompatible types in assignment (expression has type "B", variable has type "A") [case testInheritInitFromObject] - x = None # type: A -x = A() -x = B() +if int(): + x = A() +if int(): + x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") class A(object): pass class B(object): pass -[out] -main:4: error: Incompatible types in assignment (expression has type "B", variable has type "A") [case testImplicitInheritInitFromObject] - x = None # type: A o = None # type: object -x = o # E: Incompatible types in assignment (expression has type "object", variable has type "A") -x = A() -o = x +if int(): + x = o # E: Incompatible types in assignment (expression has type "object", variable has type "A") +if int(): + x = A() +if int(): + o = x class A: pass class B: pass [out] @@ -71,8 +69,10 @@ main:3: error: Incompatible types in assignment (expression has type "A", variab [case testDeclaredVariableInParentheses] (x) = None # type: int -x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") -x = 1 +if int(): + x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + x = 1 -- Simple functions and calling @@ -258,8 +258,9 @@ class B: pass class A: pass while A: a = None # type: A - a = A() - a = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") + if int(): + a = A() + a = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") -- # type: signatures diff --git a/test-data/unit/check-bound.test b/test-data/unit/check-bound.test index 043148f6423d..ed7ed01e40d0 100644 --- a/test-data/unit/check-bound.test +++ b/test-data/unit/check-bound.test @@ -14,19 +14,17 @@ T = TypeVar('T', bound=A) U = TypeVar('U') def f(x: T) -> T: pass def g(x: U) -> U: - return f(x) # Fail + return f(x) # E: Value of type variable "T" of "f" cannot be "U" f(A()) f(B()) -f(D()) # Fail +f(D()) # E: Value of type variable "T" of "f" cannot be "D" b = B() -b = f(b) -b = f(C()) # Fail -[out] -main:12: error: Value of type variable "T" of "f" cannot be "U" -main:16: error: Value of type variable "T" of "f" cannot be "D" -main:20: error: Incompatible types in assignment (expression has type "C", variable has type "B") +if int(): + b = f(b) +if int(): + b = f(C()) # E: Incompatible types in assignment (expression has type "C", variable has type "B") [case testBoundOnGenericClass] @@ -198,6 +196,7 @@ def foo(x: int) -> int: a = 1 b = foo(a) -b = 'a' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + b = 'a' # E: Incompatible types in assignment (expression has type "str", variable has type "int") twice(a) # E: Value of type variable "T" of "twice" cannot be "int" [builtins fixtures/args.pyi] diff --git a/test-data/unit/check-class-namedtuple.test b/test-data/unit/check-class-namedtuple.test index abc5689c71d5..04667b82f60e 100644 --- a/test-data/unit/check-class-namedtuple.test +++ b/test-data/unit/check-class-namedtuple.test @@ -103,7 +103,8 @@ s: str = n.a # E: Incompatible types in assignment (expression has type "int", i: int = n.b # E: Incompatible types in assignment (expression has type "str", \ variable has type "int") x, y = n -x = y # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + x = y # E: Incompatible types in assignment (expression has type "str", variable has type "int") [case testNewNamedTupleConstructorArgumentTypes] # flags: --python-version 3.6 @@ -131,9 +132,12 @@ class X(N): x = X(1, 2) # E: Argument 2 to "X" has incompatible type "int"; expected "str" s = '' i = 0 -s = x.a # E: Incompatible types in assignment (expression has type "int", variable has type "str") -i, s = x -s, s = x # E: Incompatible types in assignment (expression has type "int", variable has type "str") +if int(): + s = x.a # E: Incompatible types in assignment (expression has type "int", variable has type "str") +if int(): + i, s = x +if int(): + s, s = x # E: Incompatible types in assignment (expression has type "int", variable has type "str") [case testNewNamedTupleSelfTypeWithNamedTupleAsBase] # flags: --python-version 3.6 diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 037572115d55..994ffc7bf485 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -594,25 +594,25 @@ a.__init__(a) # E: Cannot access "__init__" directly [case testDeepInheritanceHierarchy] import typing -d = C() # type: D # Fail -d = B() # Fail -d = A() # Fail -d = D2() # Fail +d = C() # type: D # E: Incompatible types in assignment (expression has type "C", variable has type "D") +if int(): + d = B() # E: Incompatible types in assignment (expression has type "B", variable has type "D") +if int(): + d = A() # E: Incompatible types in assignment (expression has type "A", variable has type "D") +if int(): + d = D2() # E: Incompatible types in assignment (expression has type "D2", variable has type "D") a = D() # type: A -a = D2() +if int(): + a = D2() b = D() # type: B -b = D2() +if int(): + b = D2() class A: pass class B(A): pass class C(B): pass class D(C): pass class D2(C): pass -[out] -main:2: error: Incompatible types in assignment (expression has type "C", variable has type "D") -main:3: error: Incompatible types in assignment (expression has type "B", variable has type "D") -main:4: error: Incompatible types in assignment (expression has type "A", variable has type "D") -main:5: error: Incompatible types in assignment (expression has type "D2", variable has type "D") -- Attribute access in class body @@ -626,9 +626,11 @@ class A: x = B() y = x b = x # type: B - b = x + if int(): + b = x c = x # type: A # E: Incompatible types in assignment (expression has type "B", variable has type "A") - c = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") + if int(): + c = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") [out] [case testMethodRefInClassBody] @@ -638,10 +640,12 @@ class A: def f(self) -> None: pass g = f h = f # type: Callable[[A], None] - h = f - g = h + if int(): + h = f + g = h ff = f # type: Callable[[B], None] # E: Incompatible types in assignment (expression has type "Callable[[A], None]", variable has type "Callable[[B], None]") - g = ff # E: Incompatible types in assignment (expression has type "Callable[[B], None]", variable has type "Callable[[A], None]") + if int(): + g = ff # E: Incompatible types in assignment (expression has type "Callable[[B], None]", variable has type "Callable[[A], None]") [out] @@ -655,10 +659,14 @@ class B: pass class A: for x in [A()]: y = x - y = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") - x = A() - y = A() - x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + if int(): + y = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + if int(): + x = A() + if int(): + y = A() + if int(): + x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") [builtins fixtures/for.pyi] [out] @@ -805,8 +813,10 @@ import typing class A: class B: pass b = B() - b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") - b = B(b) # E: Too many arguments for "B" + if int(): + b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") + if int(): + b = B(b) # E: Too many arguments for "B" [out] [case testConstructNestedClassWithCustomInit] @@ -832,13 +842,15 @@ def f() -> None: [out] [case testExternalReferenceToClassWithinClass] - class A: class B: pass b = None # type: A.B -b = A.B() -b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") -b = A.B(b) # E: Too many arguments for "B" +if int(): + b = A.B() +if int(): + b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + b = A.B(b) # E: Too many arguments for "B" [case testAliasNestedClass] class Outer: @@ -1528,8 +1540,9 @@ class A: pass class B: pass a = None # type: A b = None # type: B -b = a # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a = b +if int(): + b = a # E: Incompatible types in assignment (expression has type "A", variable has type "B") + a = b [case testDucktypeTransitivityDecorator] from typing import _promote @@ -1540,8 +1553,9 @@ class B: pass class C: pass a = None # type: A c = None # type: C -c = a # E: Incompatible types in assignment (expression has type "A", variable has type "C") -a = c +if int(): + c = a # E: Incompatible types in assignment (expression has type "A", variable has type "C") + a = c -- Hard coded type promotions @@ -2448,7 +2462,8 @@ class B: integer = 0 b = B() b.at = '3' # E: Incompatible types in assignment (expression has type "str", variable has type "int") -integer = b.at # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + integer = b.at # E: Incompatible types in assignment (expression has type "str", variable has type "int") -- CallableType objects -- ---------------- @@ -2461,9 +2476,12 @@ b = B() a() # E: Too few arguments for "__call__" of "A" a(a, a) # E: Too many arguments for "__call__" of "A" -a = a(a) -a = a(b) # E: Argument 1 to "__call__" of "A" has incompatible type "B"; expected "A" -b = a(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + a = a(a) +if int(): + a = a(b) # E: Argument 1 to "__call__" of "A" has incompatible type "B"; expected "A" +if int(): + b = a(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") class A: def __call__(self, x: A) -> A: @@ -2564,9 +2582,11 @@ C('') # E: No overload variant of "C" matches argument type "str" \ import typing class A(int): pass n = 0 -n = A() +if int(): + n = A() a = A() -a = 0 # E: Incompatible types in assignment (expression has type "int", variable has type "A") +if int(): + a = 0 # E: Incompatible types in assignment (expression has type "int", variable has type "A") [case testForwardReferenceToNestedClass] def f(o: 'B.C') -> None: @@ -5080,8 +5100,10 @@ b: B c: C d: D d = A() # E: Incompatible types in assignment (expression has type "A", variable has type "D") -d = B() # E: Incompatible types in assignment (expression has type "B", variable has type "D") -d = C() # E: Incompatible types in assignment (expression has type "C", variable has type "D") +if int(): + d = B() # E: Incompatible types in assignment (expression has type "B", variable has type "D") +if int(): + d = C() # E: Incompatible types in assignment (expression has type "C", variable has type "D") a = D() b = D() c = D() diff --git a/test-data/unit/check-columns.test b/test-data/unit/check-columns.test index 89e7c4e1e5aa..b92c0177ed73 100644 --- a/test-data/unit/check-columns.test +++ b/test-data/unit/check-columns.test @@ -48,10 +48,11 @@ A().f(1, 'hello', 'hi') # E:1: Too many arguments for "f" of "A" # flags: --show-column-numbers x = 15 y = 'hello' -x = 2; y = x; y += 1 +if int(): + x = 2; y = x; y += 1 [out] -main:4:8: error: Incompatible types in assignment (expression has type "int", variable has type "str") -main:4:15: error: Unsupported operand types for + ("str" and "int") +main:5:12: error: Incompatible types in assignment (expression has type "int", variable has type "str") +main:5:19: error: Unsupported operand types for + ("str" and "int") [case testColumnsSimpleIsinstance] # flags: --show-column-numbers diff --git a/test-data/unit/check-dynamic-typing.test b/test-data/unit/check-dynamic-typing.test index 21a6217c807c..8aebefbb947c 100644 --- a/test-data/unit/check-dynamic-typing.test +++ b/test-data/unit/check-dynamic-typing.test @@ -20,12 +20,17 @@ from typing import Any d = None # type: Any a, b = None, None # type: (A, B) -d, a = b, b # E: Incompatible types in assignment (expression has type "B", variable has type "A") -d, d = d, d, d # E: Too many values to unpack (2 expected, 3 provided) - -a, b = d, d -d, d = a, b -a, b = d +if int(): + d, a = b, b # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + d, d = d, d, d # E: Too many values to unpack (2 expected, 3 provided) + +if int(): + a, b = d, d +if int(): + d, d = a, b +if int(): + a, b = d s, t = d class A: pass @@ -40,12 +45,17 @@ class B: pass from typing import Any a, b = None, None # type: (A, B) -b = f(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + b = f(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a = f(a) -a = f(b) -a = f(None) -a = f(f) +if int(): + a = f(a) +if int(): + a = f(b) +if int(): + a = f(None) +if int(): + a = f(f) def f(x: Any) -> 'A': pass @@ -79,25 +89,43 @@ n = 0 d in a # E: Unsupported right operand type for in ("A") d and a d or a -c = d and b # E: Incompatible types in assignment (expression has type "Union[Any, bool]", variable has type "C") -c = d or b # E: Incompatible types in assignment (expression has type "Union[Any, bool]", variable has type "C") - -c = d + a -c = d - a -c = d * a -c = d / a -c = d // a -c = d % a -c = d ** a -b = d == a -b = d != a -b = d < a -b = d <= a -b = d > a -b = d >= a -b = d in c -b = d and b -b = d or b +if int(): + c = d and b # E: Incompatible types in assignment (expression has type "Union[Any, bool]", variable has type "C") +if int(): + c = d or b # E: Incompatible types in assignment (expression has type "Union[Any, bool]", variable has type "C") + +if int(): + c = d + a +if int(): + c = d - a +if int(): + c = d * a +if int(): + c = d / a +if int(): + c = d // a +if int(): + c = d % a +if int(): + c = d ** a +if int(): + b = d == a +if int(): + b = d != a +if int(): + b = d < a +if int(): + b = d <= a +if int(): + b = d > a +if int(): + b = d >= a +if int(): + b = d in c +if int(): + b = d and b +if int(): + b = d or b class A: pass class C: @@ -122,22 +150,37 @@ n = 0 a and d a or d -c = a in d -c = b and d # E: Incompatible types in assignment (expression has type "Union[bool, Any]", variable has type "C") -c = b or d # E: Incompatible types in assignment (expression has type "Union[bool, Any]", variable has type "C") -b = a + d -b = a / d - -c = a + d -c = a - d -c = a * d -c = a / d -c = a // d -c = a % d -c = a ** d -b = a in d -b = b and d -b = b or d +if int(): + c = a in d +if int(): + c = b and d # E: Incompatible types in assignment (expression has type "Union[bool, Any]", variable has type "C") +if int(): + c = b or d # E: Incompatible types in assignment (expression has type "Union[bool, Any]", variable has type "C") +if int(): + b = a + d +if int(): + b = a / d + +if int(): + c = a + d +if int(): + c = a - d +if int(): + c = a * d +if int(): + c = a / d +if int(): + c = a // d +if int(): + c = a % d +if int(): + c = a ** d +if int(): + b = a in d +if int(): + b = b and d +if int(): + b = b or d class A: def __add__(self, a: 'A') -> 'C': @@ -174,9 +217,11 @@ from typing import Any d = None # type: Any a = None # type: A b = None # type: bool -a = not d # E: Incompatible types in assignment (expression has type "bool", variable has type "A") -b = not d -a = -d +if int(): + a = not d # E: Incompatible types in assignment (expression has type "bool", variable has type "A") +if int(): + b = not d + a = -d class A: pass [builtins fixtures/bool.pyi] [out] @@ -186,10 +231,13 @@ from typing import Any d = None # type: Any a = None # type: A -a = d.foo(a()) # E: "A" not callable +if int(): + a = d.foo(a()) # E: "A" not callable -a = d.x -a = d.foo(a, a) +if int(): + a = d.x +if int(): + a = d.foo(a, a) d.x = a d.x.y.z # E: "A" has no attribute "y" @@ -201,10 +249,12 @@ from typing import Any d = None # type: Any a = None # type: A -a = d[a()] # E: "A" not callable +if int(): + a = d[a()] # E: "A" not callable d[a()] = a # E: "A" not callable -a = d[a] +if int(): + a = d[a] d[a] = a d[a], d[a] = a, a @@ -215,8 +265,10 @@ from typing import Tuple, Any t2 = None # type: Tuple[A, A] d = None # type: Any -t2 = (d, d, d) # E: Incompatible types in assignment (expression has type "Tuple[Any, Any, Any]", variable has type "Tuple[A, A]") -t2 = (d, d) +if int(): + t2 = (d, d, d) # E: Incompatible types in assignment (expression has type "Tuple[Any, Any, Any]", variable has type "Tuple[A, A]") +if int(): + t2 = (d, d) class A: pass [builtins fixtures/tuple.pyi] @@ -228,10 +280,14 @@ class B: pass d = None # type: Any a = None # type: A b = None # type: B -b = cast(A, d) # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a = cast(A, d) -b = cast(Any, d) -a = cast(Any, f()) +if int(): + b = cast(A, d) # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + a = cast(A, d) +if int(): + b = cast(Any, d) +if int(): + a = cast(Any, f()) def f() -> None: pass [case testCompatibilityOfDynamicWithOtherTypes] @@ -302,11 +358,14 @@ h = None # type: Callable[[A], None] f() # E: Too few arguments for "f" f(x, x) # E: Too many arguments for "f" -g = f # E: Incompatible types in assignment (expression has type "Callable[[Any], Any]", variable has type "Callable[[], None]") +if int(): + g = f # E: Incompatible types in assignment (expression has type "Callable[[Any], Any]", variable has type "Callable[[], None]") f(a) f(x) -a = f(a) -h = f +if int(): + a = f(a) +if int(): + h = f def f(x): pass @@ -319,13 +378,19 @@ g1 = None # type: Callable[[A], None] g2 = None # type: Callable[[A, A], None] a = None # type: A -g1 = f0 # E: Incompatible types in assignment (expression has type "Callable[[], Any]", variable has type "Callable[[A], None]") -g2 = f0 # E: Incompatible types in assignment (expression has type "Callable[[], Any]", variable has type "Callable[[A, A], None]") -g0 = f2 # E: Incompatible types in assignment (expression has type "Callable[[Any, Any], Any]", variable has type "Callable[[], None]") -g1 = f2 # E: Incompatible types in assignment (expression has type "Callable[[Any, Any], Any]", variable has type "Callable[[A], None]") - -g0 = g0 -g2 = f2 +if int(): + g1 = f0 # E: Incompatible types in assignment (expression has type "Callable[[], Any]", variable has type "Callable[[A], None]") +if int(): + g2 = f0 # E: Incompatible types in assignment (expression has type "Callable[[], Any]", variable has type "Callable[[A, A], None]") +if int(): + g0 = f2 # E: Incompatible types in assignment (expression has type "Callable[[Any, Any], Any]", variable has type "Callable[[], None]") +if int(): + g1 = f2 # E: Incompatible types in assignment (expression has type "Callable[[Any, Any], Any]", variable has type "Callable[[A], None]") + +if int(): + g0 = g0 +if int(): + g2 = f2 f0() f2(a, a) @@ -345,12 +410,15 @@ g2 = None # type: Callable[[A, A], None] g3 = None # type: Callable[[A, A, A], None] g4 = None # type: Callable[[A, A, A, A], None] -f01(a, a) # Fail -f13() # Fail -f13(a, a, a, a) # Fail -g2 = f01 # Fail -g0 = f13 # Fail -g4 = f13 # Fail +f01(a, a) # E: Too many arguments for "f01" +f13() # E: Too few arguments for "f13" +f13(a, a, a, a) # E: Too many arguments for "f13" +if int(): + g2 = f01 # E: Incompatible types in assignment (expression has type "Callable[[Any], Any]", variable has type "Callable[[A, A], None]") +if int(): + g0 = f13 # E: Incompatible types in assignment (expression has type "Callable[[Any, Any, Any], Any]", variable has type "Callable[[], None]") +if int(): + g4 = f13 # E: Incompatible types in assignment (expression has type "Callable[[Any, Any, Any], Any]", variable has type "Callable[[A, A, A, A], None]") f01() f01(a) @@ -358,27 +426,24 @@ f13(a) f13(a, a) f13(a, a, a) -g0 = f01 -g1 = f01 -g1 = f13 -g2 = f13 -g3 = f13 +if int(): + g0 = f01 +if int(): + g1 = f01 +if int(): + g1 = f13 +if int(): + g2 = f13 +if int(): + g3 = f13 def f01(x = b): pass def f13(x, y = b, z = b): pass class A: pass class B: pass -[out] -main:10: error: Too many arguments for "f01" -main:11: error: Too few arguments for "f13" -main:12: error: Too many arguments for "f13" -main:13: error: Incompatible types in assignment (expression has type "Callable[[Any], Any]", variable has type "Callable[[A, A], None]") -main:14: error: Incompatible types in assignment (expression has type "Callable[[Any, Any, Any], Any]", variable has type "Callable[[], None]") -main:15: error: Incompatible types in assignment (expression has type "Callable[[Any, Any, Any], Any]", variable has type "Callable[[A, A, A, A], None]") [case testSkipTypeCheckingWithImplicitSignature] - a = None # type: A def f(): a() @@ -392,7 +457,6 @@ class A: pass [builtins fixtures/bool.pyi] [case testSkipTypeCheckingWithImplicitSignatureAndDefaultArgs] - a = None # type: A def f(x=a()): a() @@ -407,17 +471,22 @@ g1 = None # type: Callable[[A], None] g2 = None # type: Callable[[A, A], None] a = None # type: A -g0 = a.f # E: Incompatible types in assignment (expression has type "Callable[[Any], Any]", variable has type "Callable[[], None]") -g2 = a.f # E: Incompatible types in assignment (expression has type "Callable[[Any], Any]", variable has type "Callable[[A, A], None]") -a = a.f # E: Incompatible types in assignment (expression has type "Callable[[Any], Any]", variable has type "A") +if int(): + g0 = a.f # E: Incompatible types in assignment (expression has type "Callable[[Any], Any]", variable has type "Callable[[], None]") +if int(): + g2 = a.f # E: Incompatible types in assignment (expression has type "Callable[[Any], Any]", variable has type "Callable[[A, A], None]") +if int(): + a = a.f # E: Incompatible types in assignment (expression has type "Callable[[Any], Any]", variable has type "A") class A: def g(self) -> None: a = self.f(a) def f(self, x): pass -g1 = a.f -a = a.f(a) +if int(): + g1 = a.f +if int(): + a = a.f(a) [case testSkipTypeCheckingImplicitMethod] @@ -434,10 +503,13 @@ g0 = None # type: Callable[[], None] g1 = None # type: Callable[[A], None] a = None # type: A -g0 = a.f # E: Incompatible types in assignment (expression has type "Callable[[Any], Any]", variable has type "Callable[[], None]") +if int(): + g0 = a.f # E: Incompatible types in assignment (expression has type "Callable[[Any], Any]", variable has type "Callable[[], None]") -g1 = a.f -a = a.f(a) +if int(): + g1 = a.f +if int(): + a = a.f(a) class B: def f(self, x): @@ -475,17 +547,16 @@ f1 = None # type: Callable[[A], A] f2 = None # type: Callable[[A, A], A] a = None # type: A -A(a) # Fail -f1 = A # Fail +A(a) # E: Too few arguments for "A" +if int(): + f1 = A # E: Incompatible types in assignment (expression has type "Type[A]", variable has type "Callable[[A], A]") A(a, a) -f2 = A +if int(): + f2 = A class A: def __init__(self, a, b): pass -[out] -main:6: error: Too few arguments for "A" -main:7: error: Incompatible types in assignment (expression has type "Type[A]", variable has type "Callable[[A], A]") [case testUsingImplicitTypeObjectWithIs] @@ -510,6 +581,8 @@ t3 = None # type: Tuple[Any, Any] t4 = None # type: Tuple[A, A] t5 = None # type: Tuple[Any, Any, Any] +def f(): t1, t2, t3, t4, t5 # Prevent redefinition + t3 = t5 # E: Incompatible types in assignment (expression has type "Tuple[Any, Any, Any]", variable has type "Tuple[Any, Any]") t5 = t4 # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[Any, Any, Any]") @@ -571,7 +644,8 @@ from typing import Any, Callable f1 = None # type: Callable[[Any], None] f2 = None # type: Callable[[Any, Any], None] -f1 = f2 # E: Incompatible types in assignment (expression has type "Callable[[Any, Any], None]", variable has type "Callable[[Any], None]") +if int(): + f1 = f2 # E: Incompatible types in assignment (expression has type "Callable[[Any, Any], None]", variable has type "Callable[[Any], None]") -- Overriding diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index c04b1210faa4..e3d33174bbb0 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -8,7 +8,8 @@ class Medal(Enum): bronze = 3 reveal_type(Medal.bronze) # E: Revealed type is '__main__.Medal' m = Medal.gold -m = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Medal") +if int(): + m = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Medal") [case testEnumFromEnumMetaBasics] from enum import EnumMeta @@ -21,7 +22,8 @@ class Medal(metaclass=EnumMeta): def __init__(self, *args): pass reveal_type(Medal.bronze) # E: Revealed type is '__main__.Medal' m = Medal.gold -m = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Medal") +if int(): + m = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Medal") [case testEnumFromEnumMetaSubclass] from enum import EnumMeta @@ -34,7 +36,8 @@ class Medal(Achievement): def __init__(self, *args): pass reveal_type(Medal.bronze) # E: Revealed type is '__main__.Medal' m = Medal.gold -m = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Medal") +if int(): + m = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Medal") [case testEnumFromEnumMetaGeneric] from enum import EnumMeta @@ -74,11 +77,11 @@ class N(IntEnum): x = 1 y = 1 n = 1 -n = N.x # Subclass of int, so it's okay +if int(): + n = N.x # Subclass of int, so it's okay s = '' -s = N.y -[out] -main:8: error: Incompatible types in assignment (expression has type "N", variable has type "str") +if int(): + s = N.y # E: Incompatible types in assignment (expression has type "N", variable has type "str") [case testIntEnum_functionTakingIntEnum] from enum import IntEnum @@ -220,10 +223,10 @@ class C(Flag): a = 1 b = 2 x = C.a -x = 1 -x = x | C.b -[out] -main:6: error: Incompatible types in assignment (expression has type "int", variable has type "C") +if int(): + x = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "C") +if int(): + x = x | C.b [case testEnumIntFlag] from enum import IntFlag @@ -231,10 +234,10 @@ class C(IntFlag): a = 1 b = 2 x = C.a -x = 1 -x = x | C.b -[out] -main:6: error: Incompatible types in assignment (expression has type "int", variable has type "C") +if int(): + x = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "C") +if int(): + x = x | C.b [case testAnonymousEnum] from enum import Enum @@ -258,9 +261,8 @@ class B: a = 1 x = A.E.a y = B.E.a -x = y -[out] -main:10: error: Incompatible types in assignment (expression has type "__main__.B.E", variable has type "__main__.A.E") +if int(): + x = y # E: Incompatible types in assignment (expression has type "__main__.B.E", variable has type "__main__.A.E") [case testFunctionalEnumString] from enum import Enum, IntEnum @@ -388,9 +390,8 @@ class B: E = Enum('E', 'a b') x = A.E.a y = B.E.a -x = y -[out] -main:8: error: Incompatible types in assignment (expression has type "__main__.B.E", variable has type "__main__.A.E") +if int(): + x = y # E: Incompatible types in assignment (expression has type "__main__.B.E", variable has type "__main__.A.E") [case testFunctionalEnumProtocols] from enum import IntEnum @@ -454,4 +455,3 @@ main:3: error: Revealed type is 'm.F' [out2] main:2: error: Revealed type is 'm.E' main:3: error: Revealed type is 'm.F' - diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index fd2bc496deb1..ef335b47fea3 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -33,26 +33,34 @@ class B(A): pass [case testIntLiteral] a = 0 b = None # type: A -b = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "A") -a = 1 +if int(): + b = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "A") +if int(): + a = 1 class A: pass [case testStrLiteral] a = '' b = None # type: A -b = 'x' # E: Incompatible types in assignment (expression has type "str", variable has type "A") -a = 'x' -a = r"x" -a = """foo""" +if int(): + b = 'x' # E: Incompatible types in assignment (expression has type "str", variable has type "A") +if int(): + a = 'x' +if int(): + a = r"x" +if int(): + a = """foo""" class A: pass [case testFloatLiteral] a = 0.0 b = None # type: A -b = 1.1 # E: Incompatible types in assignment (expression has type "float", variable has type "A") -a = 1.1 +if str(): + b = 1.1 # E: Incompatible types in assignment (expression has type "float", variable has type "A") +if str(): + a = 1.1 class A: pass [file builtins.py] @@ -66,8 +74,10 @@ class str: pass [case testComplexLiteral] a = 0.0j b = None # type: A -b = 1.1j # E: Incompatible types in assignment (expression has type "complex", variable has type "A") -a = 1.1j +if str(): + b = 1.1j # E: Incompatible types in assignment (expression has type "complex", variable has type "A") +if str(): + a = 1.1j class A: pass [file builtins.py] @@ -80,10 +90,14 @@ class str: pass [case testBytesLiteral] b, a = None, None # type: (bytes, A) -b = b'foo' -b = br"foo" -b = b'''foo''' -a = b'foo' # E: Incompatible types in assignment (expression has type "bytes", variable has type "A") +if str(): + b = b'foo' +if str(): + b = br"foo" +if str(): + b = b'''foo''' +if str(): + a = b'foo' # E: Incompatible types in assignment (expression has type "bytes", variable has type "A") class A: pass [file builtins.py] class object: @@ -96,9 +110,11 @@ class str: pass [case testUnicodeLiteralInPython3] s = None # type: str -s = u'foo' +if int(): + s = u'foo' b = None # type: bytes -b = u'foo' # E: Incompatible types in assignment (expression has type "str", variable has type "bytes") +if int(): + b = u'foo' # E: Incompatible types in assignment (expression has type "str", variable has type "bytes") [builtins fixtures/primitives.pyi] @@ -107,28 +123,15 @@ b = u'foo' # E: Incompatible types in assignment (expression has type "str", var [case testAdd] - a, b, c = None, None, None # type: (A, B, C) -c = a + c # Fail -a = a + b # Fail -c = b + a # Fail -c = a + b - -class A: - def __add__(self, x: 'B') -> 'C': pass -class B: pass -class C: pass -[out] -main:3: error: Unsupported operand types for + ("A" and "C") -main:4: error: Incompatible types in assignment (expression has type "C", variable has type "A") -main:5: error: Unsupported left operand type for + ("B") -[case testAdd] - -a, b, c = None, None, None # type: (A, B, C) -c = a + c # Fail -a = a + b # Fail -c = b + a # Fail -c = a + b +if int(): + c = a + c # E: Unsupported operand types for + ("A" and "C") +if int(): + a = a + b # E: Incompatible types in assignment (expression has type "C", variable has type "A") +if int(): + c = b + a # E: Unsupported left operand type for + ("B") +if int(): + c = a + b class A: def __add__(self, x: 'B') -> 'C': @@ -137,18 +140,17 @@ class B: pass class C: pass -[out] -main:3: error: Unsupported operand types for + ("A" and "C") -main:4: error: Incompatible types in assignment (expression has type "C", variable has type "A") -main:5: error: Unsupported left operand type for + ("B") [case testSub] - a, b, c = None, None, None # type: (A, B, C) -c = a - c # Fail -a = a - b # Fail -c = b - a # Fail -c = a - b +if int(): + c = a - c # E: Unsupported operand types for - ("A" and "C") +if int(): + a = a - b # E: Incompatible types in assignment (expression has type "C", variable has type "A") +if int(): + c = b - a # E: Unsupported left operand type for - ("B") +if int(): + c = a - b class A: def __sub__(self, x: 'B') -> 'C': @@ -157,18 +159,17 @@ class B: pass class C: pass -[out] -main:3: error: Unsupported operand types for - ("A" and "C") -main:4: error: Incompatible types in assignment (expression has type "C", variable has type "A") -main:5: error: Unsupported left operand type for - ("B") [case testMul] - a, b, c = None, None, None # type: (A, B, C) -c = a * c # Fail -a = a * b # Fail -c = b * a # Fail -c = a * b +if int(): + c = a * c # E: Unsupported operand types for * ("A" and "C") +if int(): + a = a * b # E: Incompatible types in assignment (expression has type "C", variable has type "A") +if int(): + c = b * a # E: Unsupported left operand type for * ("B") +if int(): + c = a * b class A: def __mul__(self, x: 'B') -> 'C': @@ -177,17 +178,17 @@ class B: pass class C: pass -[out] -main:3: error: Unsupported operand types for * ("A" and "C") -main:4: error: Incompatible types in assignment (expression has type "C", variable has type "A") -main:5: error: Unsupported left operand type for * ("B") [case testMatMul] a, b, c = None, None, None # type: (A, B, C) -c = a @ c # E: Unsupported operand types for @ ("A" and "C") -a = a @ b # E: Incompatible types in assignment (expression has type "C", variable has type "A") -c = b @ a # E: Unsupported left operand type for @ ("B") -c = a @ b +if int(): + c = a @ c # E: Unsupported operand types for @ ("A" and "C") +if int(): + a = a @ b # E: Incompatible types in assignment (expression has type "C", variable has type "A") +if int(): + c = b @ a # E: Unsupported left operand type for @ ("B") +if int(): + c = a @ b class A: def __matmul__(self, x: 'B') -> 'C': @@ -198,12 +199,14 @@ class C: pass [case testDiv] - a, b, c = None, None, None # type: (A, B, C) -c = a / c # Fail -a = a / b # Fail -c = b / a # Fail -c = a / b +if int(): + c = a / c # E: Unsupported operand types for / ("A" and "C") + a = a / b # E: Incompatible types in assignment (expression has type "C", variable has type "A") +if int(): + c = b / a # E: Unsupported left operand type for / ("B") +if int(): + c = a / b class A: def __truediv__(self, x: 'B') -> 'C': @@ -212,18 +215,16 @@ class B: pass class C: pass -[out] -main:3: error: Unsupported operand types for / ("A" and "C") -main:4: error: Incompatible types in assignment (expression has type "C", variable has type "A") -main:5: error: Unsupported left operand type for / ("B") [case testIntDiv] - a, b, c = None, None, None # type: (A, B, C) -c = a // c # Fail -a = a // b # Fail -c = b // a # Fail -c = a // b +if int(): + c = a // c # E: Unsupported operand types for // ("A" and "C") + a = a // b # E: Incompatible types in assignment (expression has type "C", variable has type "A") +if int(): + c = b // a # E: Unsupported left operand type for // ("B") +if int(): + c = a // b class A: def __floordiv__(self, x: 'B') -> 'C': @@ -232,18 +233,17 @@ class B: pass class C: pass -[out] -main:3: error: Unsupported operand types for // ("A" and "C") -main:4: error: Incompatible types in assignment (expression has type "C", variable has type "A") -main:5: error: Unsupported left operand type for // ("B") [case testMod] - a, b, c = None, None, None # type: (A, B, C) -c = a % c # Fail -a = a % b # Fail -c = b % a # Fail -c = a % b +if int(): + c = a % c # E: Unsupported operand types for % ("A" and "C") +if int(): + a = a % b # E: Incompatible types in assignment (expression has type "C", variable has type "A") +if int(): + c = b % a # E: Unsupported left operand type for % ("B") +if int(): + c = a % b class A: def __mod__(self, x: 'B') -> 'C': @@ -252,18 +252,17 @@ class B: pass class C: pass -[out] -main:3: error: Unsupported operand types for % ("A" and "C") -main:4: error: Incompatible types in assignment (expression has type "C", variable has type "A") -main:5: error: Unsupported left operand type for % ("B") [case testPow] - a, b, c = None, None, None # type: (A, B, C) -c = a ** c # Fail -a = a ** b # Fail -c = b ** a # Fail -c = a ** b +if int(): + c = a ** c # E: Unsupported operand types for ** ("A" and "C") +if int(): + a = a ** b # E: Incompatible types in assignment (expression has type "C", variable has type "A") +if int(): + c = b ** a # E: Unsupported left operand type for ** ("B") +if int(): + c = a ** b class A: def __pow__(self, x: 'B') -> 'C': @@ -272,10 +271,6 @@ class B: pass class C: pass -[out] -main:3: error: Unsupported operand types for ** ("A" and "C") -main:4: error: Incompatible types in assignment (expression has type "C", variable has type "A") -main:5: error: Unsupported left operand type for ** ("B") [case testMiscBinaryOperators] @@ -306,14 +301,19 @@ main:6: error: Unsupported operand types for << ("A" and "B") main:7: error: Unsupported operand types for >> ("A" and "A") [case testBooleanAndOr] - a, b = None, None # type: (A, bool) -b = b and b -b = b or b -b = b and a # E: Incompatible types in assignment (expression has type "Union[bool, A]", variable has type "bool") -b = a and b # E: Incompatible types in assignment (expression has type "Union[A, bool]", variable has type "bool") -b = b or a # E: Incompatible types in assignment (expression has type "Union[bool, A]", variable has type "bool") -b = a or b # E: Incompatible types in assignment (expression has type "Union[A, bool]", variable has type "bool") +if int(): + b = b and b +if int(): + b = b or b +if int(): + b = b and a # E: Incompatible types in assignment (expression has type "Union[bool, A]", variable has type "bool") +if int(): + b = a and b # E: Incompatible types in assignment (expression has type "Union[A, bool]", variable has type "bool") +if int(): + b = b or a # E: Incompatible types in assignment (expression has type "Union[bool, A]", variable has type "bool") +if int(): + b = a or b # E: Incompatible types in assignment (expression has type "Union[A, bool]", variable has type "bool") class A: pass [builtins fixtures/bool.pyi] @@ -344,14 +344,19 @@ reveal_type(s and b or b) # E: Revealed type is 'builtins.bool' [builtins fixtures/bool.pyi] [case testNonBooleanOr] - c, d, b = None, None, None # type: (C, D, bool) -c = c or c -c = c or d -c = d or c -b = c or c # E: Incompatible types in assignment (expression has type "C", variable has type "bool") -d = c or d # E: Incompatible types in assignment (expression has type "C", variable has type "D") -d = d or c # E: Incompatible types in assignment (expression has type "C", variable has type "D") +if int(): + c = c or c +if int(): + c = c or d +if int(): + c = d or c +if int(): + b = c or c # E: Incompatible types in assignment (expression has type "C", variable has type "bool") +if int(): + d = c or d # E: Incompatible types in assignment (expression has type "C", variable has type "D") +if int(): + d = d or c # E: Incompatible types in assignment (expression has type "C", variable has type "D") class C: pass class D(C): pass [builtins fixtures/bool.pyi] @@ -359,14 +364,22 @@ class D(C): pass [case testInOperator] from typing import Iterator, Iterable, Any a, b, c, d, e = None, None, None, None, None # type: (A, B, bool, D, Any) -c = c in a # Fail -a = b in a # Fail -c = a in b # Fail -c = b in d # Fail -c = b in a -c = a in d -c = e in d -c = a in e +if int(): + c = c in a # E: Unsupported operand types for in ("bool" and "A") +if int(): + a = b in a # E: Incompatible types in assignment (expression has type "bool", variable has type "A") +if int(): + c = a in b # E: Unsupported right operand type for in ("B") +if int(): + c = b in d # E: Unsupported operand types for in ("B" and "D") +if int(): + c = b in a +if int(): + c = a in d +if int(): + c = e in d +if int(): + c = a in e class A: def __contains__(self, x: 'B') -> bool: pass @@ -374,23 +387,26 @@ class B: pass class D(Iterable[A]): def __iter__(self) -> Iterator[A]: pass [builtins fixtures/bool.pyi] -[out] -main:3: error: Unsupported operand types for in ("bool" and "A") -main:4: error: Incompatible types in assignment (expression has type "bool", variable has type "A") -main:5: error: Unsupported right operand type for in ("B") -main:6: error: Unsupported operand types for in ("B" and "D") [case testNotInOperator] from typing import Iterator, Iterable, Any a, b, c, d, e = None, None, None, None, None # type: (A, B, bool, D, Any) -c = c not in a # Fail -a = b not in a # Fail -c = a not in b # Fail -c = b not in d # Fail -c = b not in a -c = a not in d -c = e in d -c = a in e +if int(): + c = c not in a # E: Unsupported operand types for in ("bool" and "A") +if int(): + a = b not in a # E: Incompatible types in assignment (expression has type "bool", variable has type "A") +if int(): + c = a not in b # E: Unsupported right operand type for in ("B") +if int(): + c = b not in d # E: Unsupported operand types for in ("B" and "D") +if int(): + c = b not in a +if int(): + c = a not in d +if int(): + c = e in d +if int(): + c = a in e class A: def __contains__(self, x: 'B') -> bool: pass @@ -398,47 +414,45 @@ class B: pass class D(Iterable[A]): def __iter__(self) -> Iterator[A]: pass [builtins fixtures/bool.pyi] -[out] -main:3: error: Unsupported operand types for in ("bool" and "A") -main:4: error: Incompatible types in assignment (expression has type "bool", variable has type "A") -main:5: error: Unsupported right operand type for in ("B") -main:6: error: Unsupported operand types for in ("B" and "D") [case testNonBooleanContainsReturnValue] - a, b = None, None # type: (A, bool) -b = a not in a -b = a in a +if int(): + b = a not in a +if int(): + b = a in a # E: Incompatible types in assignment (expression has type "object", variable has type "bool") class A: def __contains__(self, x: 'A') -> object: pass [builtins fixtures/bool.pyi] -[out] -main:4: error: Incompatible types in assignment (expression has type "object", variable has type "bool") [case testEq] a, b = None, None # type: (A, bool) -a = a == b # Fail -a = a != b # Fail -b = a == b -b = a != b +if int(): + a = a == b # E: Incompatible types in assignment (expression has type "bool", variable has type "A") +if int(): + a = a != b # E: Incompatible types in assignment (expression has type "bool", variable has type "A") +if int(): + b = a == b +if int(): + b = a != b class A: def __eq__(self, o: object) -> bool: pass def __ne__(self, o: object) -> bool: pass [builtins fixtures/bool.pyi] -[out] -main:3: error: Incompatible types in assignment (expression has type "bool", variable has type "A") -main:4: error: Incompatible types in assignment (expression has type "bool", variable has type "A") [case testLtAndGt] - a, b, bo = None, None, None # type: (A, B, bool) -a = a < b # Fail -a = a > b # Fail -bo = a < b -bo = a > b +if int(): + a = a < b # E: Incompatible types in assignment (expression has type "bool", variable has type "A") +if int(): + a = a > b # E: Incompatible types in assignment (expression has type "bool", variable has type "A") +if int(): + bo = a < b +if int(): + bo = a > b class A: def __lt__(self, o: 'B') -> bool: pass @@ -447,9 +461,6 @@ class B: def __lt__(self, o: 'B') -> bool: pass def __gt__(self, o: 'B') -> bool: pass [builtins fixtures/bool.pyi] -[out] -main:3: error: Incompatible types in assignment (expression has type "bool", variable has type "A") -main:4: error: Incompatible types in assignment (expression has type "bool", variable has type "A") [case testCmp_python2] @@ -504,12 +515,15 @@ class B: [builtins fixtures/bool.pyi] [case testLeAndGe] - a, b, bo = None, None, None # type: (A, B, bool) -a = a <= b # Fail -a = a >= b # Fail -bo = a <= b -bo = a >= b +if int(): + a = a <= b # E: Incompatible types in assignment (expression has type "bool", variable has type "A") +if int(): + a = a >= b # E: Incompatible types in assignment (expression has type "bool", variable has type "A") +if int(): + bo = a <= b +if int(): + bo = a >= b class A: def __le__(self, o: 'B') -> bool: pass @@ -518,9 +532,6 @@ class B: def __le__(self, o: 'B') -> bool: pass def __ge__(self, o: 'B') -> bool: pass [builtins fixtures/bool.pyi] -[out] -main:3: error: Incompatible types in assignment (expression has type "bool", variable has type "A") -main:4: error: Incompatible types in assignment (expression has type "bool", variable has type "A") [case testChainedComp] @@ -543,10 +554,11 @@ main:5: error: Unsupported operand types for > ("A" and "A") [case testChainedCompBoolRes] - a, b, bo = None, None, None # type: (A, B, bool) -bo = a < b < b -a = a < b < b # Fail +if int(): + bo = a < b < b +if int(): + a = a < b < b # E: Incompatible types in assignment (expression has type "bool", variable has type "A") class A: def __lt__(self, o: 'B') -> bool: pass @@ -555,19 +567,21 @@ class B: def __lt__(self, o: 'B') -> bool: pass def __gt__(self, o: 'B') -> bool: pass [builtins fixtures/bool.pyi] -[out] -main:4: error: Incompatible types in assignment (expression has type "bool", variable has type "A") [case testChainedCompResTyp] - x, y = None, None # type: (X, Y) a, b, p, bo = None, None, None, None # type: (A, B, P, bool) -b = y == y == y -bo = y == y == y # Fail -a = x < y -a = x < y == y # Fail -p = x < y == y +if int(): + b = y == y == y +if int(): + bo = y == y == y # E: Incompatible types in assignment (expression has type "B", variable has type "bool") +if int(): + a = x < y +if int(): + a = x < y == y # E: Incompatible types in assignment (expression has type "P", variable has type "A") +if int(): + p = x < y == y class P: pass @@ -584,34 +598,33 @@ class Y: def __gt__(self, o: 'Y') -> A: pass def __eq__(self, o: 'Y') -> B: pass [builtins fixtures/bool.pyi] -[out] -main:5: error: Incompatible types in assignment (expression has type "B", variable has type "bool") -main:7: error: Incompatible types in assignment (expression has type "P", variable has type "A") [case testIs] - a, b = None, None # type: (A, bool) -a = a is b # Fail -b = a is b -b = b is a -b = a is None +if int(): + a = a is b # E: Incompatible types in assignment (expression has type "bool", variable has type "A") +if int(): + b = a is b +if int(): + b = b is a +if int(): + b = a is None class A: pass [builtins fixtures/bool.pyi] -[out] -main:3: error: Incompatible types in assignment (expression has type "bool", variable has type "A") [case testIsNot] - a, b = None, None # type: (A, bool) -a = a is not b # Fail -b = a is not b -b = b is not a -b = a is not None +if int(): + a = a is not b # E: Incompatible types in assignment (expression has type "bool", variable has type "A") +if int(): + b = a is not b +if int(): + b = b is not a +if int(): + b = a is not None class A: pass [builtins fixtures/bool.pyi] -[out] -main:3: error: Incompatible types in assignment (expression has type "bool", variable has type "A") [case testIsRightOperand] @@ -628,20 +641,23 @@ class B: def __radd__(self, x: A) -> str: pass s = None # type: str n = None # type: int -n = A() + 1 -s = A() + B() -n = A() + B() # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + n = A() + 1 +if int(): + s = A() + B() +if int(): + n = A() + B() # E: Incompatible types in assignment (expression has type "str", variable has type "int") [case testReverseBinaryOperator2] - class A: def __add__(self, x: 'A') -> object: pass class B: def __radd__(self, x: A) -> str: pass s = None # type: str n = None # type: int -s = A() + B() -n = A() + B() # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + s = A() + B() + n = A() + B() # E: Incompatible types in assignment (expression has type "str", variable has type "int") [case testReverseBinaryOperator3] @@ -660,7 +676,6 @@ class A: pass A() + cast(Any, 1) [case testReverseComparisonOperator] - class C: def __gt__(self, x: 'A') -> object: pass class A: @@ -669,10 +684,12 @@ class B: def __gt__(self, x: A) -> str: pass s = None # type: str n = None # type: int -n = A() < C() -s = A() < B() -n = A() < B() # E: Incompatible types in assignment (expression has type "str", variable has type "int") -s = object() < B() # E: Unsupported operand types for > ("B" and "object") +if int(): + n = A() < C() + s = A() < B() +if int(): + n = A() < B() # E: Incompatible types in assignment (expression has type "str", variable has type "int") + s = object() < B() # E: Unsupported operand types for > ("B" and "object") [case testReversibleComparisonWithExtraArgument] class C: @@ -762,62 +779,60 @@ divmod('foo', d) # E: Unsupported operand types for divmod ("str" and "Decimal" [case testUnaryMinus] a, b = None, None # type: (A, B) -a = -a # Fail -b = -b # Fail -b = -a +if int(): + a = -a # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + b = -b # E: Unsupported operand type for unary - ("B") +if int(): + b = -a class A: def __neg__(self) -> 'B': pass class B: pass -[out] -main:3: error: Incompatible types in assignment (expression has type "B", variable has type "A") -main:4: error: Unsupported operand type for unary - ("B") [case testUnaryPlus] - a, b = None, None # type: (A, B) -a = +a # Fail -b = +b # Fail -b = +a +if int(): + a = +a # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + b = +b # E: Unsupported operand type for unary + ("B") +if int(): + b = +a class A: def __pos__(self) -> 'B': pass class B: pass -[out] -main:3: error: Incompatible types in assignment (expression has type "B", variable has type "A") -main:4: error: Unsupported operand type for unary + ("B") [case testUnaryNot] - a, b = None, None # type: (A, bool) -a = not b # Fail -b = not a -b = not b +if int(): + a = not b # E: Incompatible types in assignment (expression has type "bool", variable has type "A") +if int(): + b = not a +if int(): + b = not b class A: pass [builtins fixtures/bool.pyi] -[out] -main:3: error: Incompatible types in assignment (expression has type "bool", variable has type "A") [case testUnaryBitwiseNeg] - a, b = None, None # type: (A, B) -a = ~a # Fail -b = ~b # Fail -b = ~a +if int(): + a = ~a # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + b = ~b # E: Unsupported operand type for ~ ("B") +if int(): + b = ~a class A: def __invert__(self) -> 'B': pass class B: pass -[out] -main:3: error: Incompatible types in assignment (expression has type "B", variable has type "A") -main:4: error: Unsupported operand type for ~ ("B") -- Indexing @@ -827,20 +842,20 @@ main:4: error: Unsupported operand type for ~ ("B") [case testIndexing] a, b, c = None, None, None # type: (A, B, C) -c = a[c] # Fail -a = a[b] # Fail -c = b[a] # Fail -c = a[b] +if int(): + c = a[c] # E: Invalid index type "C" for "A"; expected type "B" +if int(): + a = a[b] # E: Incompatible types in assignment (expression has type "C", variable has type "A") +if int(): + c = b[a] # E: Value of type "B" is not indexable +if int(): + c = a[b] class A: def __getitem__(self, x: 'B') -> 'C': pass class B: pass class C: pass -[out] -main:3: error: Invalid index type "C" for "A"; expected type "B" -main:4: error: Incompatible types in assignment (expression has type "C", variable has type "A") -main:5: error: Value of type "B" is not indexable [case testIndexingAsLvalue] @@ -876,10 +891,14 @@ a[1] # E: No overload variant of "__getitem__" of "A" matches argument type "in # N: def __getitem__(self, C) -> str i, s = None, None # type: (int, str) -i = a[b] -s = a[b] # E: Incompatible types in assignment (expression has type "int", variable has type "str") -i = a[c] # E: Incompatible types in assignment (expression has type "str", variable has type "int") -s = a[c] +if int(): + i = a[b] +if int(): + s = a[b] # E: Incompatible types in assignment (expression has type "int", variable has type "str") +if int(): + i = a[c] # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + s = a[c] class A: @overload @@ -904,16 +923,22 @@ class B: pass class C(A): pass a, b, c = None, None, None # type: (A, B, C) -a = cast(A, a()) # E: "A" not callable -a = cast(Any, a()) # E: "A" not callable -b = cast(A, a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") - -a = cast(A, b) -a = cast(A, a) -c = cast(C, a) -a = cast(A, c) -a = cast(Any, b) -b = cast(Any, a) +if int(): + a = cast(A, a()) # E: "A" not callable +if int(): + a = cast(Any, a()) # E: "A" not callable + b = cast(A, a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") + +if int(): + a = cast(A, b) +if int(): + a = cast(A, a) + c = cast(C, a) +if int(): + a = cast(A, c) +if int(): + a = cast(Any, b) + b = cast(Any, a) [out] [case testAnyCast] @@ -933,11 +958,13 @@ main:3: error: "A" not callable [case testNoneReturnTypeBasics] - a, o = None, None # type: (A, object) -a = f() # E: "f" does not return a value -o = a() # E: Function does not return a value -o = A().g(a) # E: "g" of "A" does not return a value +if int(): + a = f() # E: "f" does not return a value +if int(): + o = a() # E: Function does not return a value +if int(): + o = A().g(a) # E: "g" of "A" does not return a value A().g(f()) # E: "f" does not return a value x: A = f() # E: "f" does not return a value f() @@ -1013,17 +1040,24 @@ class A: [case testGetSlice] - a, b = None, None # type: (A, B) -a = a[1:2] # E: Incompatible types in assignment (expression has type "B", variable has type "A") -a = a[1:] # E: Incompatible types in assignment (expression has type "B", variable has type "A") -a = a[:2] # E: Incompatible types in assignment (expression has type "B", variable has type "A") -a = a[:] # E: Incompatible types in assignment (expression has type "B", variable has type "A") - -b = a[1:2] -b = a[1:] -b = a[:2] -b = a[:] +if int(): + a = a[1:2] # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + a = a[1:] # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + a = a[:2] # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + a = a[:] # E: Incompatible types in assignment (expression has type "B", variable has type "A") + +if int(): + b = a[1:2] +if int(): + b = a[1:] +if int(): + b = a[:2] +if int(): + b = a[:] class A: def __getitem__(self, s: slice) -> 'B': pass @@ -1253,12 +1287,12 @@ u'%s' % (u'abc',) [case testTrivialLambda] from typing import Callable f = lambda: 1 # type: Callable[[], int] -f = lambda: ''.x -f = lambda: '' -[out] -main:3: error: "str" has no attribute "x" -main:4: error: Incompatible types in assignment (expression has type "Callable[[], str]", variable has type "Callable[[], int]") -main:4: error: Incompatible return value type (got "str", expected "int") +if int(): + f = lambda: ''.x # E: "str" has no attribute "x" +if int(): + f = lambda: '' \ + # E: Incompatible types in assignment (expression has type "Callable[[], str]", variable has type "Callable[[], int]") \ + # E: Incompatible return value type (got "str", expected "int") [case testVoidLambda] import typing @@ -1314,8 +1348,8 @@ def f(x: int): pass [case testListComprehensionWithNonDirectMapping] from typing import List -a = None # type: List[A] -b = None # type: List[B] +a: List[A] +b: List[B] b = [f(x) for x in a] a = [f(x) for x in a] # E: List comprehension has incompatible type List[B]; expected List[A] ([f(x) for x in b]) # E: Argument 1 to "f" has incompatible type "B"; expected "A" @@ -1385,7 +1419,7 @@ main:6: error: Incompatible types in assignment (expression has type "Dict[A, B] [case testDictionaryComprehensionWithNonDirectMapping] from typing import Dict, List, Tuple -abd = None # type: Dict[A, B] +abd: Dict[A, B] abl = None # type: List[Tuple[A, B]] abd = {a: f(b) for a, b in abl} class A: pass @@ -1407,9 +1441,11 @@ from typing import Iterator # The implementation is mostly identical to list comprehensions, so only a few # test cases is ok. a = None # type: Iterator[int] -a = (x for x in a) +if int(): + a = (x for x in a) b = None # type: Iterator[str] -b = (x for x in a) # E: Generator has incompatible item type "int"; expected "str" +if int(): + b = (x for x in a) # E: Generator has incompatible item type "int"; expected "str" [builtins fixtures/for.pyi] [case testGeneratorIncompatibleErrorMessage] @@ -1417,7 +1453,8 @@ from typing import Callable, Iterator, List a = [] # type: List[Callable[[], str]] b = None # type: Iterator[Callable[[], int]] -b = (x for x in a) # E: Generator has incompatible item type "Callable[[], str]"; expected "Callable[[], int]" +if int(): + b = (x for x in a) # E: Generator has incompatible item type "Callable[[], str]"; expected "Callable[[], int]" [builtins fixtures/list.pyi] -- Conditional expressions @@ -1428,8 +1465,10 @@ b = (x for x in a) # E: Generator has incompatible item type "Callable[[], str] import typing y = '' x = 1 if y else 2 -x = 3 -x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + x = 3 +if int(): + x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") [case testConditionalExpressionWithEmptyCondition] import typing @@ -1441,18 +1480,24 @@ import typing class A: pass class B(A): pass x = B() if bool() else A() -x = A() -x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "A") +if int(): + x = A() +if int(): + x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "A") y = A() if bool() else B() -y = A() -y = '' # E: Incompatible types in assignment (expression has type "str", variable has type "A") +if int(): + y = A() +if int(): + y = '' # E: Incompatible types in assignment (expression has type "str", variable has type "A") [builtins fixtures/bool.pyi] [case testConditionalExpressionAndTypeContext] import typing x = [1] if bool() else [] -x = [1] -x = ['x'] # E: List item 0 has incompatible type "str"; expected "int" +if int(): + x = [1] +if int(): + x = ['x'] # E: List item 0 has incompatible type "str"; expected "int" [builtins fixtures/list.pyi] [case testConditionalExpressionUnion] @@ -1563,10 +1608,12 @@ class C(B): [case testEllipsis] a = None # type: A -a = ... # E: Incompatible types in assignment (expression has type "ellipsis", variable has type "A") +if str(): + a = ... # E: Incompatible types in assignment (expression has type "ellipsis", variable has type "A") b = ... c = ... -b = c +if str(): + b = c ....__class__ ....a # E: "ellipsis" has no attribute "a" diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index b4dc45a429da..d4f4ba5333b5 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -342,17 +342,20 @@ x: Final = 1 x = 2 # E: Cannot assign to final name "x" def f() -> int: global x - x = 3 # E: Cannot assign to final name "x" + x = 3 # No error here is okay since we reported an error above return x +x2: Final = 1 +def f2() -> None: + global x2 + x2 = 1 # E: Cannot assign to final name "x2" + y = 1 -y: Final = 2 # E: Name 'y' already defined on line 10 \ - # E: Cannot redefine an existing name as final +y: Final = 2 # E: Cannot redefine an existing name as final y = 3 # No error here, first definition wins z: Final = 1 -z: Final = 2 # E: Name 'z' already defined on line 14 \ - # E: Cannot redefine an existing name as final +z: Final = 2 # E: Cannot redefine an existing name as final z = 3 # E: Cannot assign to final name "z" [out] @@ -412,7 +415,7 @@ from typing import Final class C: x: Final = 1 - x = 2 # E: Cannot assign to final name "x" + x = 2 # E: Cannot assign to final attribute "x" y = 1 y: Final = 2 # E: Cannot redefine an existing name as final diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 2892ff8c5db9..99d11c3d45b3 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -494,10 +494,12 @@ import standard, optional [file standard.py] x = 0 -x = None +if int(): + x = None [file optional.py] x = 0 -x = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") +if int(): + x = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") [file mypy.ini] [[mypy] diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index 242117b6fe61..614e13b928c2 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -12,11 +12,16 @@ from typing import Callable f = None # type: Callable[[A], B] a, b = None, None # type: (A, B) -a = f(a) # E: Incompatible types in assignment (expression has type "B", variable has type "A") -b = f(b) # E: Argument 1 has incompatible type "B"; expected "A" -b = f() # E: Too few arguments -b = f(a, a) # E: Too many arguments -b = f(a) +if int(): + a = f(a) # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + b = f(b) # E: Argument 1 has incompatible type "B"; expected "A" +if int(): + b = f() # E: Too few arguments +if int(): + b = f(a, a) # E: Too many arguments +if int(): + b = f(a) class A: pass class B: pass @@ -65,15 +70,24 @@ class B(A): pass f = None # type: Callable[[B], A] g = None # type: Callable[[A], A] # subtype of f h = None # type: Callable[[B], B] # subtype of f -g = h # E: Incompatible types in assignment (expression has type "Callable[[B], B]", variable has type "Callable[[A], A]") -h = f # E: Incompatible types in assignment (expression has type "Callable[[B], A]", variable has type "Callable[[B], B]") -h = g # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[B], B]") -g = f # E: Incompatible types in assignment (expression has type "Callable[[B], A]", variable has type "Callable[[A], A]") -f = g -f = h -f = f -g = g -h = h +if int(): + g = h # E: Incompatible types in assignment (expression has type "Callable[[B], B]", variable has type "Callable[[A], A]") +if int(): + h = f # E: Incompatible types in assignment (expression has type "Callable[[B], A]", variable has type "Callable[[B], B]") +if int(): + h = g # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[B], B]") +if int(): + g = f # E: Incompatible types in assignment (expression has type "Callable[[B], A]", variable has type "Callable[[A], A]") +if int(): + f = g +if int(): + f = h +if int(): + f = f +if int(): + g = g +if int(): + h = h [case testSubtypingFunctionsDoubleCorrespondence] @@ -112,13 +126,20 @@ ff = f gg = g hh = h -ff = gg -ff_nonames = ff -ff_nonames = f_nonames # reset -ff = ff_nonames # E: Incompatible types in assignment (expression has type "Callable[[int, str], None]", variable has type "Callable[[Arg(int, 'a'), Arg(str, 'b')], None]") -ff = f # reset -gg = ff # E: Incompatible types in assignment (expression has type "Callable[[Arg(int, 'a'), Arg(str, 'b')], None]", variable has type "Callable[[Arg(int, 'a'), DefaultArg(str, 'b')], None]") -gg = hh # E: Incompatible types in assignment (expression has type "Callable[[Arg(int, 'aa'), DefaultArg(str, 'b')], None]", variable has type "Callable[[Arg(int, 'a'), DefaultArg(str, 'b')], None]") +if int(): + ff = gg +if int(): + ff_nonames = ff +if int(): + ff_nonames = f_nonames # reset +if int(): + ff = ff_nonames # E: Incompatible types in assignment (expression has type "Callable[[int, str], None]", variable has type "Callable[[Arg(int, 'a'), Arg(str, 'b')], None]") +if int(): + ff = f # reset +if int(): + gg = ff # E: Incompatible types in assignment (expression has type "Callable[[Arg(int, 'a'), Arg(str, 'b')], None]", variable has type "Callable[[Arg(int, 'a'), DefaultArg(str, 'b')], None]") +if int(): + gg = hh # E: Incompatible types in assignment (expression has type "Callable[[Arg(int, 'aa'), DefaultArg(str, 'b')], None]", variable has type "Callable[[Arg(int, 'a'), DefaultArg(str, 'b')], None]") [case testSubtypingFunctionsArgsKwargs] from typing import Any, Callable @@ -134,17 +155,27 @@ ss_2 = specific_2 ee_def = everything ee_var = everywhere -ss_1 = ee_def -ss_1 = specific_1 -ss_2 = ee_def -ss_2 = specific_2 -ee_def = everywhere -ee_def = everything -ee_var = everything -ee_var = everywhere +if int(): + ss_1 = ee_def +if int(): + ss_1 = specific_1 +if int(): + ss_2 = ee_def +if int(): + ss_2 = specific_2 +if int(): + ee_def = everywhere +if int(): + ee_def = everything +if int(): + ee_var = everything +if int(): + ee_var = everywhere -ee_var = specific_1 # The difference between Callable[..., blah] and one with a *args: Any, **kwargs: Any is that the ... goes loosely both ways. -ee_def = specific_1 # E: Incompatible types in assignment (expression has type "Callable[[int, str], None]", variable has type "Callable[[VarArg(Any), KwArg(Any)], None]") +if int(): + ee_var = specific_1 # The difference between Callable[..., blah] and one with a *args: Any, **kwargs: Any is that the ... goes loosely both ways. +if int(): + ee_def = specific_1 # E: Incompatible types in assignment (expression has type "Callable[[int, str], None]", variable has type "Callable[[VarArg(Any), KwArg(Any)], None]") [builtins fixtures/dict.pyi] @@ -174,33 +205,42 @@ def g(a: int, b: str) -> None: pass ff = f gg = g -ff = g -gg = f # E: Incompatible types in assignment (expression has type "Callable[[int, str], None]", variable has type "Callable[[Arg(int, 'a'), Arg(str, 'b')], None]") +if int(): + ff = g +if int(): + gg = f # E: Incompatible types in assignment (expression has type "Callable[[int, str], None]", variable has type "Callable[[Arg(int, 'a'), Arg(str, 'b')], None]") [case testLackOfNamesFastparse] - - def f(__a: int, __b: str) -> None: pass def g(a: int, b: str) -> None: pass ff = f gg = g -ff = g -gg = f # E: Incompatible types in assignment (expression has type "Callable[[int, str], None]", variable has type "Callable[[Arg(int, 'a'), Arg(str, 'b')], None]") +if int(): + ff = g +if int(): + gg = f # E: Incompatible types in assignment (expression has type "Callable[[int, str], None]", variable has type "Callable[[Arg(int, 'a'), Arg(str, 'b')], None]") [case testFunctionTypeCompatibilityWithOtherTypes] from typing import Callable f = None # type: Callable[[], None] a, o = None, None # type: (A, object) -a = f # E: Incompatible types in assignment (expression has type "Callable[[], None]", variable has type "A") -f = a # E: Incompatible types in assignment (expression has type "A", variable has type "Callable[[], None]") -f = o # E: Incompatible types in assignment (expression has type "object", variable has type "Callable[[], None]") -f = f() # E: Function does not return a value +if int(): + a = f # E: Incompatible types in assignment (expression has type "Callable[[], None]", variable has type "A") +if int(): + f = a # E: Incompatible types in assignment (expression has type "A", variable has type "Callable[[], None]") +if int(): + f = o # E: Incompatible types in assignment (expression has type "object", variable has type "Callable[[], None]") +if int(): + f = f() # E: Function does not return a value -f = f -f = None -o = f +if int(): + f = f +if int(): + f = None +if int(): + o = f class A: pass @@ -208,26 +248,39 @@ class A: pass from typing import Callable f = None # type: Callable[[], None] g = None # type: Callable[[], object] -f = g # E: Incompatible types in assignment (expression has type "Callable[[], object]", variable has type "Callable[[], None]") -g = f # OK +if int(): + f = g # E: Incompatible types in assignment (expression has type "Callable[[], object]", variable has type "Callable[[], None]") +if int(): + g = f # OK -f = f -g = g +if int(): + f = f +if int(): + g = g [case testFunctionSubtypingWithMultipleArgs] from typing import Callable f = None # type: Callable[[A, A], None] g = None # type: Callable[[A, B], None] h = None # type: Callable[[B, B], None] -f = g # E: Incompatible types in assignment (expression has type "Callable[[A, B], None]", variable has type "Callable[[A, A], None]") -f = h # E: Incompatible types in assignment (expression has type "Callable[[B, B], None]", variable has type "Callable[[A, A], None]") -g = h # E: Incompatible types in assignment (expression has type "Callable[[B, B], None]", variable has type "Callable[[A, B], None]") -g = f -h = f -h = g -f = f -g = g -h = h +if int(): + f = g # E: Incompatible types in assignment (expression has type "Callable[[A, B], None]", variable has type "Callable[[A, A], None]") +if int(): + f = h # E: Incompatible types in assignment (expression has type "Callable[[B, B], None]", variable has type "Callable[[A, A], None]") +if int(): + g = h # E: Incompatible types in assignment (expression has type "Callable[[B, B], None]", variable has type "Callable[[A, B], None]") +if int(): + g = f +if int(): + h = f +if int(): + h = g +if int(): + f = f +if int(): + g = g +if int(): + h = h class A: pass class B(A): pass @@ -238,26 +291,35 @@ f = None # type: Callable[[], None] g = None # type: Callable[[A], None] h = None # type: Callable[[A, A], None] -f = g # E: Incompatible types in assignment (expression has type "Callable[[A], None]", variable has type "Callable[[], None]") -f = h # E: Incompatible types in assignment (expression has type "Callable[[A, A], None]", variable has type "Callable[[], None]") -h = f # E: Incompatible types in assignment (expression has type "Callable[[], None]", variable has type "Callable[[A, A], None]") -h = g # E: Incompatible types in assignment (expression has type "Callable[[A], None]", variable has type "Callable[[A, A], None]") +if int(): + f = g # E: Incompatible types in assignment (expression has type "Callable[[A], None]", variable has type "Callable[[], None]") +if int(): + f = h # E: Incompatible types in assignment (expression has type "Callable[[A, A], None]", variable has type "Callable[[], None]") +if int(): + h = f # E: Incompatible types in assignment (expression has type "Callable[[], None]", variable has type "Callable[[A, A], None]") +if int(): + h = g # E: Incompatible types in assignment (expression has type "Callable[[A], None]", variable has type "Callable[[A, A], None]") -f = f -g = g -h = h +if int(): + f = f +if int(): + g = g +if int(): + h = h class A: pass [out] [case testCompatibilityOfSimpleTypeObjectWithStdType] - t = None # type: type a = None # type: A -a = A # E: Incompatible types in assignment (expression has type "Type[A]", variable has type "A") -t = f # E: Incompatible types in assignment (expression has type "Callable[[], None]", variable has type "type") -t = A +if int(): + a = A # E: Incompatible types in assignment (expression has type "Type[A]", variable has type "A") +if int(): + t = f # E: Incompatible types in assignment (expression has type "Callable[[], None]", variable has type "type") +if int(): + t = A class A: def __init__(self, a: 'A') -> None: pass @@ -272,12 +334,17 @@ f = None # type: Callable[[AA], A] g = None # type: Callable[[B], B] h = None # type: Callable[[A], AA] -h = i # E: Incompatible types in assignment (expression has type overloaded function, variable has type "Callable[[A], AA]") -f = j +if int(): + h = i # E: Incompatible types in assignment (expression has type overloaded function, variable has type "Callable[[A], AA]") +if int(): + f = j -f = i -g = i -g = j +if int(): + f = i +if int(): + g = i +if int(): + g = j class A: pass class AA(A): pass @@ -308,17 +375,27 @@ g3 = None # type: Callable[[C], C] g4 = None # type: Callable[[A], B] a, b, c = None, None, None # type: (A, B, C) -b = f(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a = f(b) # E: Incompatible types in assignment (expression has type "B", variable has type "A") -b = f(c) # E: Incompatible types in assignment (expression has type "C", variable has type "B") -g4 = f # E: Incompatible types in assignment (expression has type overloaded function, variable has type "Callable[[A], B]") +if int(): + b = f(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + a = f(b) # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + b = f(c) # E: Incompatible types in assignment (expression has type "C", variable has type "B") +if int(): + g4 = f # E: Incompatible types in assignment (expression has type overloaded function, variable has type "Callable[[A], B]") -g1 = f -g2 = f -g3 = f -a = f(a) -b = f(b) -c = f(c) +if int(): + g1 = f +if int(): + g2 = f +if int(): + g3 = f +if int(): + a = f(a) +if int(): + b = f(b) +if int(): + c = f(c) class A: pass class B: pass @@ -353,7 +430,8 @@ from typing import Callable, Type class A: pass x = None # type: Callable[..., A] y = None # type: Type[A] -y = x # E: Incompatible types in assignment (expression has type "Callable[..., A]", variable has type "Type[A]") +if int(): + y = x # E: Incompatible types in assignment (expression has type "Callable[..., A]", variable has type "Type[A]") -- Default argument values -- ----------------------- @@ -362,13 +440,19 @@ y = x # E: Incompatible types in assignment (expression has type "Callable[..., [case testCallingFunctionsWithDefaultArgumentValues] a, b = None, None # type: (A, B) -a = f() # E: Incompatible types in assignment (expression has type "B", variable has type "A") -b = f(b) # E: Argument 1 to "f" has incompatible type "B"; expected "Optional[A]" -b = f(a, a) # E: Too many arguments for "f" +if int(): + a = f() # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + b = f(b) # E: Argument 1 to "f" has incompatible type "B"; expected "Optional[A]" +if int(): + b = f(a, a) # E: Too many arguments for "f" -b = f() -b = f(a) -b = f(AA()) +if int(): + b = f() +if int(): + b = f(a) +if int(): + b = f(AA()) def f(x: 'A' = None) -> 'B': pass diff --git a/test-data/unit/check-generic-subtyping.test b/test-data/unit/check-generic-subtyping.test index 9c5d242368a8..12982df07f8e 100644 --- a/test-data/unit/check-generic-subtyping.test +++ b/test-data/unit/check-generic-subtyping.test @@ -13,12 +13,15 @@ ac = None # type: A[C] ad = None # type: A[D] b = None # type: B -b = ad # E: Incompatible types in assignment (expression has type "A[D]", variable has type "B") -ad = b # E: Incompatible types in assignment (expression has type "B", variable has type "A[D]") -b = ac # E: Incompatible types in assignment (expression has type "A[C]", variable has type "B") +if int(): + b = ad # E: Incompatible types in assignment (expression has type "A[D]", variable has type "B") + ad = b # E: Incompatible types in assignment (expression has type "B", variable has type "A[D]") +if int(): + b = ac # E: Incompatible types in assignment (expression has type "A[C]", variable has type "B") -b = b -ac = b +if int(): + b = b + ac = b class C: pass class A(Generic[T]): pass @@ -32,13 +35,17 @@ a = None # type: A bc = None # type: B[C] bd = None # type: B[D] -bc = bd # E: Incompatible types in assignment (expression has type "B[D]", variable has type "B[C]") -bd = bc # E: Incompatible types in assignment (expression has type "B[C]", variable has type "B[D]") -bc = a # E: Incompatible types in assignment (expression has type "A", variable has type "B[C]") -bd = a # E: Incompatible types in assignment (expression has type "A", variable has type "B[D]") +if int(): + bc = bd # E: Incompatible types in assignment (expression has type "B[D]", variable has type "B[C]") + bd = bc # E: Incompatible types in assignment (expression has type "B[C]", variable has type "B[D]") +if int(): + bc = a # E: Incompatible types in assignment (expression has type "A", variable has type "B[C]") + bd = a # E: Incompatible types in assignment (expression has type "A", variable has type "B[D]") -a = bc -a = bd +if int(): + a = bc +if int(): + a = bd class A: pass class B(A, Generic[T]): pass @@ -54,15 +61,19 @@ ad = None # type: A[D] bcc = None # type: B[C, C] bdc = None # type: B[D, C] -ad = bcc # E: Incompatible types in assignment (expression has type "B[C, C]", variable has type "A[D]") -ad = bdc # E: Incompatible types in assignment (expression has type "B[D, C]", variable has type "A[D]") -bcc = ac # E: Incompatible types in assignment (expression has type "A[C]", variable has type "B[C, C]") -bdc = ac # E: Incompatible types in assignment (expression has type "A[C]", variable has type "B[D, C]") +if int(): + ad = bcc # E: Incompatible types in assignment (expression has type "B[C, C]", variable has type "A[D]") +if int(): + ad = bdc # E: Incompatible types in assignment (expression has type "B[D, C]", variable has type "A[D]") + bcc = ac # E: Incompatible types in assignment (expression has type "A[C]", variable has type "B[C, C]") + bdc = ac # E: Incompatible types in assignment (expression has type "A[C]", variable has type "B[D, C]") -bcc = bcc -bdc = bdc -ac = bcc -ac = bdc +if int(): + bcc = bcc + bdc = bdc + ac = bcc +if int(): + ac = bdc class A(Generic[T]): pass class B(A[S], Generic[T, S]): pass @@ -82,12 +93,15 @@ cef = None # type: C[E, F] cff = None # type: C[F, F] cfe = None # type: C[F, E] -ae = cef # E: Incompatible types in assignment (expression has type "C[E, F]", variable has type "A[A[E]]") -af = cfe # E: Incompatible types in assignment (expression has type "C[F, E]", variable has type "A[A[F]]") +if int(): + ae = cef # E: Incompatible types in assignment (expression has type "C[E, F]", variable has type "A[A[E]]") + af = cfe # E: Incompatible types in assignment (expression has type "C[F, E]", variable has type "A[A[F]]") -ae = cfe -af = cef -af = cff +if int(): + ae = cfe + af = cef +if int(): + af = cff class A(Generic[T]): pass class B(A[S], Generic[T, S]): pass @@ -280,9 +294,10 @@ a = None # type: A bc = None # type: B[C] bd = None # type: B[D] -a = bc # E: Incompatible types in assignment (expression has type "B[C]", variable has type "A") -bc = a -bd = a +if int(): + a = bc # E: Incompatible types in assignment (expression has type "B[C]", variable has type "A") + bc = a + bd = a class B(Generic[T]): pass class A(B): pass @@ -427,12 +442,14 @@ adc = None # type: A[D, C] ic = None # type: I[C] id = None # type: I[D] -ic = acd # E: Incompatible types in assignment (expression has type "A[C, D]", variable has type "I[C]") -id = adc # E: Incompatible types in assignment (expression has type "A[D, C]", variable has type "I[D]") -adc = ic # E: Incompatible types in assignment (expression has type "I[C]", variable has type "A[D, C]") +if int(): + ic = acd # E: Incompatible types in assignment (expression has type "A[C, D]", variable has type "I[C]") + id = adc # E: Incompatible types in assignment (expression has type "A[D, C]", variable has type "I[D]") + adc = ic # E: Incompatible types in assignment (expression has type "I[C]", variable has type "A[D, C]") -ic = adc -id = acd +if int(): + ic = adc + id = acd class I(Generic[T]): @abstractmethod @@ -451,14 +468,17 @@ class I(Generic[S]): pass class B(I[C]): pass class A(B): pass -ie = a # E: Incompatible types in assignment (expression has type "A", variable has type "I[E]") -a = ic # E: Incompatible types in assignment (expression has type "I[C]", variable has type "A") -a = id # E: Incompatible types in assignment (expression has type "I[D]", variable has type "A") -a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") -id = a # E: Incompatible types in assignment (expression has type "A", variable has type "I[D]") +if int(): + ie = a # E: Incompatible types in assignment (expression has type "A", variable has type "I[E]") + a = ic # E: Incompatible types in assignment (expression has type "I[C]", variable has type "A") +if int(): + a = id # E: Incompatible types in assignment (expression has type "I[D]", variable has type "A") +if int(): + a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") + id = a # E: Incompatible types in assignment (expression has type "A", variable has type "I[D]") -ic = a -b = a + ic = a + b = a class C: pass class D: pass @@ -483,11 +503,14 @@ from abc import abstractmethod, ABCMeta t = TypeVar('t') a, i, j = None, None, None # type: (A[object], I[object], J[object]) (ii, jj) = (i, j) -ii = a -jj = a -jj = i -a = i # E: Incompatible types in assignment (expression has type "I[object]", variable has type "A[object]") -a = j # E: Incompatible types in assignment (expression has type "J[object]", variable has type "A[object]") +if int(): + ii = a + jj = a +if int(): + jj = i + a = i # E: Incompatible types in assignment (expression has type "I[object]", variable has type "A[object]") +if int(): + a = j # E: Incompatible types in assignment (expression has type "J[object]", variable has type "A[object]") class J(Generic[t]): pass class X(metaclass=ABCMeta): pass @@ -528,8 +551,9 @@ T = TypeVar('T') a = None # type: A ic, id = None, None # type: (I[C], I[D]) -id = a # Fail -ic = a +if int(): + id = a # E: Incompatible types in assignment (expression has type "A", variable has type "I[D]") + ic = a class I(Generic[T]): @abstractmethod @@ -545,8 +569,6 @@ class A(B): def f(self, a: 'C', b: 'C') -> None: pass class C: pass class D: pass -[out] -main:7: error: Incompatible types in assignment (expression has type "A", variable has type "I[D]") [case testSubclassingGenericABCWithDeepHierarchy2] from typing import Any, TypeVar, Generic @@ -704,8 +726,9 @@ a = None # type: G[A] b = None # type: G[B] c = None # type: G[C] -b = a # E: Incompatible types in assignment (expression has type "G[A]", variable has type "G[B]") -b = c +if int(): + b = a # E: Incompatible types in assignment (expression has type "G[A]", variable has type "G[B]") + b = c [builtins fixtures/bool.pyi] [out] @@ -722,8 +745,9 @@ a = None # type: G[A] b = None # type: G[B] c = None # type: G[C] -b = a -b = c # E: Incompatible types in assignment (expression has type "G[C]", variable has type "G[B]") +if int(): + b = a + b = c # E: Incompatible types in assignment (expression has type "G[C]", variable has type "G[B]") [builtins fixtures/bool.pyi] [out] @@ -740,8 +764,9 @@ a = None # type: G[A] b = None # type: G[B] c = None # type: G[C] -b = a # E: Incompatible types in assignment (expression has type "G[A]", variable has type "G[B]") -b = c # E: Incompatible types in assignment (expression has type "G[C]", variable has type "G[B]") +if int(): + b = a # E: Incompatible types in assignment (expression has type "G[A]", variable has type "G[B]") + b = c # E: Incompatible types in assignment (expression has type "G[C]", variable has type "G[B]") [builtins fixtures/bool.pyi] [out] diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 54f7e5fbd434..8cb21af3ce30 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -6,16 +6,15 @@ from typing import TypeVar, Generic T = TypeVar('T') a, b, c = None, None, None # type: (A[B], B, C) -c = a.f() # Fail -b = a.f() +if int(): + c = a.f() # E: Incompatible types in assignment (expression has type "B", variable has type "C") + b = a.f() class A(Generic[T]): def f(self) -> T: pass class B: pass class C: pass -[out] -main:4: error: Incompatible types in assignment (expression has type "B", variable has type "C") [case testGenericMethodArgument] from typing import TypeVar, Generic @@ -69,18 +68,18 @@ main:4: error: Incompatible types in assignment (expression has type "C", variab from typing import TypeVar, Generic T = TypeVar('T') b, bb, c = None, None, None # type: (A[B], A[B], A[C]) -c = b # Fail -b = c # Fail +if int(): + c = b # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[C]") + b = c # E: Incompatible types in assignment (expression has type "A[C]", variable has type "A[B]") -b = b -b = bb +if int(): + b = b +if int(): + b = bb class A(Generic[T]): pass class B: pass class C(B): pass -[out] -main:4: error: Incompatible types in assignment (expression has type "A[B]", variable has type "A[C]") -main:5: error: Incompatible types in assignment (expression has type "A[C]", variable has type "A[B]") [case testGenericTypeCompatibilityWithAny] from typing import Any, TypeVar, Generic @@ -104,19 +103,18 @@ a = None # type: A[B] b = None # type: A[B] c = None # type: A[C] -a.v = c # Fail -c = a.v # Fail +a.v = c # E: Incompatible types in assignment (expression has type "A[C]", variable has type "A[B]") +if int(): + c = a.v # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[C]") a.v = b -b = a.v +if int(): + b = a.v class A(Generic[T]): v = None # type: A[T] class B: pass class C: pass -[out] -main:7: error: Incompatible types in assignment (expression has type "A[C]", variable has type "A[B]") -main:8: error: Incompatible types in assignment (expression has type "A[B]", variable has type "A[C]") [case testMultipleGenericTypeParametersWithMemberVars] from typing import TypeVar, Generic @@ -126,20 +124,19 @@ a = None # type: A[B, C] s = None # type: B t = None # type: C -t = a.s # Fail -s = a.t # Fail +if int(): + t = a.s # E: Incompatible types in assignment (expression has type "B", variable has type "C") + s = a.t # E: Incompatible types in assignment (expression has type "C", variable has type "B") -s = a.s -t = a.t +if int(): + s = a.s + t = a.t class A(Generic[S, T]): s = None # type: S t = None # type: T class B: pass class C: pass -[out] -main:8: error: Incompatible types in assignment (expression has type "B", variable has type "C") -main:9: error: Incompatible types in assignment (expression has type "C", variable has type "B") [case testMultipleGenericTypeParametersWithMethods] from typing import TypeVar, Generic @@ -169,12 +166,15 @@ bc = None # type: A[B, C] bb = None # type: A[B, B] cb = None # type: A[C, B] -bb = bc # Fail -bb = cb # Fail -bc = bb # Fail +if int(): + bb = bc # E: Incompatible types in assignment (expression has type "A[B, C]", variable has type "A[B, B]") +if int(): + bb = cb # E: Incompatible types in assignment (expression has type "A[C, B]", variable has type "A[B, B]") + bc = bb # E: Incompatible types in assignment (expression has type "A[B, B]", variable has type "A[B, C]") -bb = bb -bc = bc +if int(): + bb = bb + bc = bc class A(Generic[S, T]): s = None # type: S @@ -182,10 +182,6 @@ class A(Generic[S, T]): class B: pass class C(B):pass -[out] -main:8: error: Incompatible types in assignment (expression has type "A[B, C]", variable has type "A[B, B]") -main:9: error: Incompatible types in assignment (expression has type "A[C, B]", variable has type "A[B, B]") -main:10: error: Incompatible types in assignment (expression has type "A[B, B]", variable has type "A[B, C]") -- Simple generic type bodies @@ -266,13 +262,16 @@ a = None # type: A[B, C] b = None # type: B c = None # type: C -b = a + b # Fail -c = a + c # Fail -c = a[c] # Fail -b = a[b] # Fail +if int(): + b = a + b # E: Incompatible types in assignment (expression has type "C", variable has type "B") + c = a + c # E: Unsupported operand types for + ("A[B, C]" and "C") +if int(): + c = a[c] # E: Incompatible types in assignment (expression has type "B", variable has type "C") + b = a[b] # E: Invalid index type "B" for "A[B, C]"; expected type "C" -c = a + b -b = a[c] +if int(): + c = a + b + b = a[c] class A(Generic[S, T]): def __add__(self, a: S) -> T: pass @@ -280,11 +279,6 @@ class A(Generic[S, T]): class B: pass class C: pass -[out] -main:8: error: Incompatible types in assignment (expression has type "C", variable has type "B") -main:9: error: Unsupported operand types for + ("A[B, C]" and "C") -main:10: error: Incompatible types in assignment (expression has type "B", variable has type "C") -main:11: error: Invalid index type "B" for "A[B, C]"; expected type "C" [case testOperatorAssignmentWithIndexLvalue1] from typing import TypeVar, Generic @@ -345,11 +339,13 @@ aac = None # type: A[A[C]] ab = None # type: A[B] ac = None # type: A[C] -ac = aab.x # Fail -ac.y = aab # Fail +if int(): + ac = aab.x # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[C]") +ac.y = aab # E: Incompatible types in assignment (expression has type "A[A[B]]", variable has type "A[A[C]]") -ab = aab.x -ac = aac.x +if int(): + ab = aab.x + ac = aac.x ab.y = aab ac.y = aac @@ -361,9 +357,6 @@ class B: pass class C: pass -[out] -main:8: error: Incompatible types in assignment (expression has type "A[B]", variable has type "A[C]") -main:9: error: Incompatible types in assignment (expression has type "A[A[B]]", variable has type "A[A[C]]") -- Generic functions @@ -978,9 +971,12 @@ class C: a = A # This is a variable b = Union[int, str] # This is an alias c: Type[object] = Iterable[int] # This is however also a variable - a = B - b = int # E: Cannot assign multiple types to name "b" without an explicit "Type[...]" annotation - c = int + if int(): + a = B + if int(): + b = int # E: Cannot assign multiple types to name "b" without an explicit "Type[...]" annotation + if int(): + c = int def f(self, x: a) -> None: pass # E: Invalid type "__main__.C.a" def g(self, x: b) -> None: pass def h(self, x: c) -> None: pass # E: Invalid type "__main__.C.c" @@ -1260,9 +1256,12 @@ list_a = [a] list_b = [b] list_b2 = [b2] -a, b = list_a # E: Incompatible types in assignment (expression has type "A", variable has type "B") -b, a = list_a # E: Incompatible types in assignment (expression has type "A", variable has type "B") -b2, b2 = list_b # E: Incompatible types in assignment (expression has type "B", variable has type "B2") +if int(): + a, b = list_a # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + b, a = list_a # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + b2, b2 = list_b # E: Incompatible types in assignment (expression has type "B", variable has type "B2") a, a = list_a b, b2, b = list_b2 @@ -1417,13 +1416,17 @@ def f(a: List[T]) -> T: pass a, b = None, None # type: (A, B) -b = f([a]) # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a = f([b]) # E: Incompatible types in assignment (expression has type "B", variable has type "A") -a = f(b) # E: Incompatible types in assignment (expression has type "B", variable has type "A") - -a = f([a]) -b = f([b]) -b = f(b) +if int(): + b = f([a]) # E: Incompatible types in assignment (expression has type "A", variable has type "B") + a = f([b]) # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + a = f(b) # E: Incompatible types in assignment (expression has type "B", variable has type "A") + +if int(): + a = f([a]) + b = f([b]) +if int(): + b = f(b) [builtins fixtures/list.pyi] @@ -1448,8 +1451,10 @@ class A: T = TypeVar('T') def g(self, x: T) -> T: pass a = A().g(1) -a = 1 -a = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + a = 1 +if int(): + a = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") [case testGenericInnerClass] from typing import TypeVar, Generic @@ -1502,28 +1507,44 @@ def f3(x: B) -> B: ... def f4(x: int) -> A: ... y1 = f1 -y1 = f1 -y1 = f2 -y1 = f3 -y1 = f4 # E: Incompatible types in assignment (expression has type "Callable[[int], A]", variable has type "Callable[[A], A]") +if int(): + y1 = f1 +if int(): + y1 = f2 +if int(): + y1 = f3 +if int(): + y1 = f4 # E: Incompatible types in assignment (expression has type "Callable[[int], A]", variable has type "Callable[[A], A]") y2 = f2 -y2 = f2 -y2 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[A], B]") -y2 = f3 # E: Incompatible types in assignment (expression has type "Callable[[B], B]", variable has type "Callable[[A], B]") -y2 = f4 # E: Incompatible types in assignment (expression has type "Callable[[int], A]", variable has type "Callable[[A], B]") +if int(): + y2 = f2 +if int(): + y2 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[A], B]") +if int(): + y2 = f3 # E: Incompatible types in assignment (expression has type "Callable[[B], B]", variable has type "Callable[[A], B]") +if int(): + y2 = f4 # E: Incompatible types in assignment (expression has type "Callable[[int], A]", variable has type "Callable[[A], B]") y3 = f3 -y3 = f3 -y3 = f1 -y3 = f2 -y3 = f4 # E: Incompatible types in assignment (expression has type "Callable[[int], A]", variable has type "Callable[[B], B]") +if int(): + y3 = f3 +if int(): + y3 = f1 +if int(): + y3 = f2 +if int(): + y3 = f4 # E: Incompatible types in assignment (expression has type "Callable[[int], A]", variable has type "Callable[[B], B]") y4 = f4 -y4 = f4 -y4 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[int], A]") -y4 = f2 -y4 = f3 # E: Incompatible types in assignment (expression has type "Callable[[B], B]", variable has type "Callable[[int], A]") +if int(): + y4 = f4 +if int(): + y4 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[int], A]") +if int(): + y4 = f2 +if int(): + y4 = f3 # E: Incompatible types in assignment (expression has type "Callable[[B], B]", variable has type "Callable[[int], A]") [case testSubtypingWithGenericInnerFunctions] from typing import TypeVar diff --git a/test-data/unit/check-ignore.test b/test-data/unit/check-ignore.test index ad183ac1fe2b..c17befa6e0f4 100644 --- a/test-data/unit/check-ignore.test +++ b/test-data/unit/check-ignore.test @@ -63,8 +63,10 @@ tmp/m.py:1: error: invalid syntax [case testIgnoreAssignmentTypeError] x = 1 -x = '' # type: ignore -x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + x = '' # type: ignore +if int(): + x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") [case testIgnoreInvalidOverride] class A: diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 75fd1bff9592..1651053539bd 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -1110,13 +1110,14 @@ class C: pass [file c/submodule.py] val = 3 # type: int -val = "foo" +if int(): + val = "foo" [builtins fixtures/module_all.pyi] [rechecked main, c, c.submodule] [stale c] [out2] -tmp/c/submodule.py:2: error: Incompatible types in assignment (expression has type "str", variable has type "int") +tmp/c/submodule.py:3: error: Incompatible types in assignment (expression has type "str", variable has type "int") tmp/main.py:7: error: "C" has no attribute "foo" [case testIncrementalRemoteError] diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test index 632cbeba0b42..3a93c2a7e585 100644 --- a/test-data/unit/check-inference-context.test +++ b/test-data/unit/check-inference-context.test @@ -11,9 +11,12 @@ ab = None # type: A[B] ao = None # type: A[object] b = None # type: B -ao = f() -ab = f() -b = f() # E: Incompatible types in assignment (expression has type "A[]", variable has type "B") +if int(): + ao = f() +if int(): + ab = f() +if int(): + b = f() # E: Incompatible types in assignment (expression has type "A[]", variable has type "B") def f() -> 'A[T]': pass @@ -27,9 +30,12 @@ ab = None # type: A[B] ao = None # type: A[object] b = None # type: B -ao = A() -ab = A() -b = A() # E: Incompatible types in assignment (expression has type "A[]", variable has type "B") +if int(): + ao = A() +if int(): + ab = A() +if int(): + b = A() # E: Incompatible types in assignment (expression has type "A[]", variable has type "B") class A(Generic[T]): pass class B: pass @@ -43,13 +49,19 @@ ab = None # type: A[B] ao = None # type: A[object] ac = None # type: A[C] -ac = f(b) # E: Argument 1 to "f" has incompatible type "B"; expected "C" -ab = f(c) # E: Argument 1 to "f" has incompatible type "C"; expected "B" +if int(): + ac = f(b) # E: Argument 1 to "f" has incompatible type "B"; expected "C" +if int(): + ab = f(c) # E: Argument 1 to "f" has incompatible type "C"; expected "B" -ao = f(b) -ab = f(b) -ao = f(c) -ac = f(c) +if int(): + ao = f(b) +if int(): + ab = f(b) +if int(): + ao = f(c) +if int(): + ac = f(c) def f(a: T) -> 'A[T]': pass @@ -152,21 +164,23 @@ o = None # type: object ab = None # type: A[B] ao = None # type: A[object] -ab, ao = f(b) # Fail -ao, ab = f(b) # Fail +if int(): + ab, ao = f(b) # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") +if int(): + ao, ab = f(b) # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") -ao, ao = f(b) -ab, ab = f(b) -ao, ao = f(o) +if int(): + ao, ao = f(b) +if int(): + ab, ab = f(b) +if int(): + ao, ao = f(o) def f(a: T) -> 'Tuple[A[T], A[T]]': pass class A(Generic[T]): pass class B: pass [builtins fixtures/tuple.pyi] -[out] -main:8: error: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") -main:9: error: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") [case testInferenceWithTypeVariableTwiceInReturnTypeAndMultipleVariables] from typing import TypeVar, Tuple, Generic @@ -177,14 +191,21 @@ o = None # type: object ab = None # type: A[B] ao = None # type: A[object] -ao, ao, ab = f(b, b) # Fail -ao, ab, ao = g(b, b) # Fail -ao, ab, ab, ab = h(b, b) # Fail -ab, ab, ao, ab = h(b, b) # Fail - -ao, ab, ab = f(b, b) -ab, ab, ao = g(b, b) -ab, ab, ab, ab = h(b, b) +if int(): + ao, ao, ab = f(b, b) # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") +if int(): + ao, ab, ao = g(b, b) # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") +if int(): + ao, ab, ab, ab = h(b, b) # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") +if int(): + ab, ab, ao, ab = h(b, b) # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") + +if int(): + ao, ab, ab = f(b, b) +if int(): + ab, ab, ao = g(b, b) +if int(): + ab, ab, ab, ab = h(b, b) def f(a: S, b: T) -> 'Tuple[A[S], A[T], A[T]]': pass def g(a: S, b: T) -> 'Tuple[A[S], A[S], A[T]]': pass @@ -193,11 +214,6 @@ def h(a: S, b: T) -> 'Tuple[A[S], A[S], A[T], A[T]]': pass class A(Generic[T]): pass class B: pass [builtins fixtures/tuple.pyi] -[out] -main:9: error: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") -main:10: error: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") -main:11: error: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") -main:12: error: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") -- Multiple tvar instances in arguments @@ -214,14 +230,21 @@ b = None # type: B c = None # type: C o = None # type: object -ab = f(b, o) # E: Argument 2 to "f" has incompatible type "object"; expected "B" -ab = f(o, b) # E: Argument 1 to "f" has incompatible type "object"; expected "B" -ac = f(b, c) # E: Argument 1 to "f" has incompatible type "B"; expected "C" -ac = f(c, b) # E: Argument 2 to "f" has incompatible type "B"; expected "C" - -ao = f(b, c) -ao = f(c, b) -ab = f(c, b) +if int(): + ab = f(b, o) # E: Argument 2 to "f" has incompatible type "object"; expected "B" +if int(): + ab = f(o, b) # E: Argument 1 to "f" has incompatible type "object"; expected "B" +if int(): + ac = f(b, c) # E: Argument 1 to "f" has incompatible type "B"; expected "C" +if int(): + ac = f(c, b) # E: Argument 2 to "f" has incompatible type "B"; expected "C" + +if int(): + ao = f(b, c) +if int(): + ao = f(c, b) +if int(): + ab = f(c, b) def f(a: T, b: T) -> 'A[T]': pass @@ -243,11 +266,13 @@ ao = None # type: A[object] b = None # type: B o = None # type: object -aab = f(f(o)) # E: Argument 1 to "f" has incompatible type "object"; expected "B" +if int(): + aab = f(f(o)) # E: Argument 1 to "f" has incompatible type "object"; expected "B" -aab = f(f(b)) -aao = f(f(b)) -ao = f(f(b)) +if int(): + aab = f(f(b)) + aao = f(f(b)) + ao = f(f(b)) def f(a: T) -> 'A[T]': pass @@ -262,10 +287,12 @@ ao = None # type: A[object] b = None # type: B o = None # type: object -ab = f(g(o)) # E: Argument 1 to "g" has incompatible type "object"; expected "B" +if int(): + ab = f(g(o)) # E: Argument 1 to "g" has incompatible type "object"; expected "B" -ab = f(g(b)) -ao = f(g(b)) +if int(): + ab = f(g(b)) + ao = f(g(b)) def f(a: T) -> T: pass @@ -282,12 +309,16 @@ ao = None # type: A[object] b = None # type: B o = None # type: object -ab = f(g(o), g(b)) # E: Argument 1 to "g" has incompatible type "object"; expected "B" -ab = f(g(b), g(o)) # E: Argument 1 to "g" has incompatible type "object"; expected "B" +if int(): + ab = f(g(o), g(b)) # E: Argument 1 to "g" has incompatible type "object"; expected "B" +if int(): + ab = f(g(b), g(o)) # E: Argument 1 to "g" has incompatible type "object"; expected "B" -ab = f(g(b), g(b)) -ao = f(g(b), g(o)) -ao = f(g(o), g(b)) +if int(): + ab = f(g(b), g(b)) + ao = f(g(b), g(o)) +if int(): + ao = f(g(o), g(b)) def f(a: T, b: T) -> T: pass @@ -313,10 +344,13 @@ ab = None # type: A[B] ac = None # type: A[C] ab.g(f(o)) # E: Argument 1 to "f" has incompatible type "object"; expected "B" -ac = f(b).g(f(c)) # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[C]") -ac = f(c).g(f(b)) # E: Argument 1 to "f" has incompatible type "B"; expected "C" +if int(): + ac = f(b).g(f(c)) # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[C]") +if int(): + ac = f(c).g(f(b)) # E: Argument 1 to "f" has incompatible type "B"; expected "C" -ab = f(b).g(f(c)) +if int(): + ab = f(b).g(f(c)) ab.g(f(c)) def f(a: T) -> 'A[T]': pass @@ -337,6 +371,7 @@ from typing import List aa = None # type: List[A] ao = None # type: List[object] a = None # type: A +def f(): a, aa, ao # Prevent redefinition a = [] # E: Incompatible types in assignment (expression has type "List[]", variable has type "A") @@ -353,6 +388,7 @@ ab = None # type: List[B] ao = None # type: List[object] a = None # type: A b = None # type: B +def f(): aa, ab, ao # Prevent redefinition aa = [b] # E: List item 0 has incompatible type "B"; expected "A" ab = [a] # E: List item 0 has incompatible type "A"; expected "B" @@ -374,6 +410,7 @@ ab = None # type: List[B] ao = None # type: List[object] a = None # type: A b = None # type: B +def f(): ab, aa, ao # Prevent redefinition ab = [b, a] # E: List item 1 has incompatible type "A"; expected "B" ab = [a, b] # E: List item 0 has incompatible type "A"; expected "B" @@ -405,6 +442,7 @@ aab = None # type: List[List[B]] ab = None # type: List[B] b = None # type: B o = None # type: object +def f(): aao, aab # Prevent redefinition aao = [[o], ab] # E: List item 1 has incompatible type "List[B]"; expected "List[object]" aab = [[], [o]] # E: List item 0 has incompatible type "object"; expected "B" @@ -534,7 +572,8 @@ class set(Generic[t]): def __init__(self, iterable: Iterable[t]) -> None: pass b = bool() l = set([b]) -l = set([object()]) # E: List item 0 has incompatible type "object"; expected "bool" +if int(): + l = set([object()]) # E: List item 0 has incompatible type "object"; expected "bool" [builtins fixtures/for.pyi] @@ -559,8 +598,9 @@ class C(Generic[s, t]): pass [case testInferLambdaArgumentTypeUsingContext] from typing import Callable f = None # type: Callable[[B], A] -f = lambda x: x.o -f = lambda x: x.x # E: "B" has no attribute "x" +if int(): + f = lambda x: x.o + f = lambda x: x.x # E: "B" has no attribute "x" class A: pass class B: o = None # type: A @@ -568,8 +608,9 @@ class B: [case testInferLambdaReturnTypeUsingContext] from typing import List, Callable f = None # type: Callable[[], List[A]] -f = lambda: [] -f = lambda: [B()] # E: List item 0 has incompatible type "B"; expected "A" +if int(): + f = lambda: [] + f = lambda: [B()] # E: List item 0 has incompatible type "B"; expected "A" class A: pass class B: pass [builtins fixtures/list.pyi] @@ -695,11 +736,16 @@ a = m # type: List[A] # E: Incompatible types in assignment (expression has type [case testOrOperationInferredFromContext] from typing import List a, b, c = None, None, None # type: (List[A], List[B], List[C]) -a = a or [] -a = [] or a -b = b or [C()] -a = a or b # E: Incompatible types in assignment (expression has type "Union[List[A], List[B]]", variable has type "List[A]") -b = b or c # E: Incompatible types in assignment (expression has type "Union[List[B], List[C]]", variable has type "List[B]") +if int(): + a = a or [] +if int(): + a = [] or a +if int(): + b = b or [C()] +if int(): + a = a or b # E: Incompatible types in assignment (expression has type "Union[List[A], List[B]]", variable has type "List[A]") +if int(): + b = b or c # E: Incompatible types in assignment (expression has type "Union[List[B], List[C]]", variable has type "List[B]") class A: pass class B: pass @@ -717,8 +763,10 @@ t = TypeVar('t') s = TypeVar('s') # Some type variables can be inferred using context, but not all of them. a = None # type: List[A] -a = f(A(), B()) -a = f(B(), B()) # E: Argument 1 to "f" has incompatible type "B"; expected "A" +if int(): + a = f(A(), B()) +if int(): + a = f(B(), B()) # E: Argument 1 to "f" has incompatible type "B"; expected "A" def f(a: s, b: t) -> List[s]: pass class A: pass class B: pass @@ -730,8 +778,10 @@ s = TypeVar('s') t = TypeVar('t') # Like testSomeTypeVarsInferredFromContext, but tvars in different order. a = None # type: List[A] -a = f(A(), B()) -a = f(B(), B()) # E: Argument 1 to "f" has incompatible type "B"; expected "A" +if int(): + a = f(A(), B()) +if int(): + a = f(B(), B()) # E: Argument 1 to "f" has incompatible type "B"; expected "A" def f(a: s, b: t) -> List[s]: pass class A: pass class B: pass @@ -752,8 +802,10 @@ class A: pass from typing import List i = None # type: List[int] s = None # type: List[str] -i = i = [] -i = s = [] # E: Incompatible types in assignment (expression has type "List[str]", variable has type "List[int]") +if int(): + i = i = [] +if int(): + i = s = [] # E: Incompatible types in assignment (expression has type "List[str]", variable has type "List[int]") [builtins fixtures/list.pyi] [case testContextForAttributeDeclaredInInit] @@ -770,8 +822,9 @@ a.x = [''] # E: List item 0 has incompatible type "str"; expected "int" [case testListMultiplyInContext] from typing import List a = None # type: List[int] -a = [None] * 3 -a = [''] * 3 # E: List item 0 has incompatible type "str"; expected "int" +if int(): + a = [None] * 3 + a = [''] * 3 # E: List item 0 has incompatible type "str"; expected "int" [builtins fixtures/list.pyi] [case testUnionTypeContext] diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 4ae034e4f773..1158d02f6f02 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -6,15 +6,16 @@ import typing x = A() y = B() -x = B() # Fail -x = A() -x = y # Fail -x = x +if int(): + x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + x = A() +if int(): + x = y # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + x = x class A: pass class B: pass -[out] -main:4: error: Incompatible types in assignment (expression has type "B", variable has type "A") -main:6: error: Incompatible types in assignment (expression has type "B", variable has type "A") [case testInferSimpleLvarType] import typing @@ -378,9 +379,12 @@ class Nums(Iterable[int]): def __iter__(self): pass def __next__(self): pass a, b = Nums() -a = b = 1 -a = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") -b = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + a = b = 1 +if int(): + a = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + b = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") [builtins fixtures/for.pyi] @@ -395,13 +399,16 @@ a = None # type: A b = None # type: B c = None # type: Tuple[A, object] -b = id(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a = id(b) # E: Incompatible types in assignment (expression has type "B", variable has type "A") -a = id(c) # E: Incompatible types in assignment (expression has type "Tuple[A, object]", variable has type "A") +if int(): + b = id(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") + a = id(b) # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + a = id(c) # E: Incompatible types in assignment (expression has type "Tuple[A, object]", variable has type "A") -a = id(a) -b = id(b) -c = id(c) +if int(): + a = id(a) + b = id(b) + c = id(c) def id(a: T) -> T: pass @@ -448,10 +455,14 @@ T = TypeVar('T') a = None # type: A b = None # type: B -b = f(a, b) # E: Incompatible types in assignment (expression has type "A", variable has type "B") -b = f(b, a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a = f(a, b) -a = f(b, a) +if int(): + b = f(a, b) # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + b = f(b, a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + a = f(a, b) +if int(): + a = f(b, a) def f(a: T, b: T) -> T: pass @@ -467,23 +478,24 @@ taa = None # type: Tuple[A, A] tab = None # type: Tuple[A, B] tba = None # type: Tuple[B, A] -taa = f(a, b) # Fail -taa = f(b, a) # Fail -tba = f(a, b) # Fail +if int(): + taa = f(a, b) # E: Argument 2 to "f" has incompatible type "B"; expected "A" +if int(): + taa = f(b, a) # E: Argument 1 to "f" has incompatible type "B"; expected "A" +if int(): + tba = f(a, b) # E: Argument 1 to "f" has incompatible type "A"; expected "B" \ + # E: Argument 2 to "f" has incompatible type "B"; expected "A" -tab = f(a, b) -tba = f(b, a) +if int(): + tab = f(a, b) +if int(): + tba = f(b, a) def f(a: T, b: S) -> Tuple[T, S]: pass class A: pass class B: pass [builtins fixtures/tuple.pyi] -[out] -main:9: error: Argument 2 to "f" has incompatible type "B"; expected "A" -main:10: error: Argument 1 to "f" has incompatible type "B"; expected "A" -main:11: error: Argument 1 to "f" has incompatible type "A"; expected "B" -main:11: error: Argument 2 to "f" has incompatible type "B"; expected "A" [case testConstraintSolvingWithSimpleGenerics] from typing import TypeVar, Generic @@ -492,18 +504,23 @@ ao = None # type: A[object] ab = None # type: A[B] ac = None # type: A[C] -ab = f(ao) # E: Argument 1 to "f" has incompatible type "A[object]"; expected "A[B]" -ao = f(ab) # E: Argument 1 to "f" has incompatible type "A[B]"; expected "A[object]" -ab = f(ac) # E: Argument 1 to "f" has incompatible type "A[C]"; expected "A[B]" -ab = g(ao) # E: Argument 1 to "g" has incompatible type "A[object]"; expected "A[B]" -ao = g(ab) # E: Argument 1 to "g" has incompatible type "A[B]"; expected "A[object]" +if int(): + ab = f(ao) # E: Argument 1 to "f" has incompatible type "A[object]"; expected "A[B]" + ao = f(ab) # E: Argument 1 to "f" has incompatible type "A[B]"; expected "A[object]" +if int(): + ab = f(ac) # E: Argument 1 to "f" has incompatible type "A[C]"; expected "A[B]" +if int(): + ab = g(ao) # E: Argument 1 to "g" has incompatible type "A[object]"; expected "A[B]" + ao = g(ab) # E: Argument 1 to "g" has incompatible type "A[B]"; expected "A[object]" -ab = f(ab) -ac = f(ac) -ao = f(ao) +if int(): + ab = f(ab) + ac = f(ac) + ao = f(ao) -ab = g(ab) -ao = g(ao) +if int(): + ab = g(ab) + ao = g(ao) def f(a: 'A[T]') -> 'A[T]': pass @@ -535,13 +552,19 @@ T = TypeVar('T') a = None # type: A o = None # type: object -a = f(o) # E: Incompatible types in assignment (expression has type "object", variable has type "A") -a = g(a, o) # E: Incompatible types in assignment (expression has type "object", variable has type "A") +if int(): + a = f(o) # E: Incompatible types in assignment (expression has type "object", variable has type "A") +if int(): + a = g(a, o) # E: Incompatible types in assignment (expression has type "object", variable has type "A") -o = f() -o = f(o) -a = f(a) -a = g(a) +if int(): + o = f() +if int(): + o = f(o) +if int(): + a = f(a) +if int(): + a = g(a) def f(a: T = None) -> T: pass def g(a: T, b: T = None) -> T: pass @@ -658,9 +681,11 @@ def f(x: bool) -> A: pass def mymap(f: Callable[[t], s], a: List[t]) -> List[s]: pass l = mymap(f, [b]) -l = [A()] +if int(): + l = [A()] lb = [b] -l = lb # E: Incompatible types in assignment (expression has type "List[bool]", variable has type "List[A]") +if int(): + l = lb # E: Incompatible types in assignment (expression has type "List[bool]", variable has type "List[A]") [builtins fixtures/for.pyi] [case testGenericFunctionWithTypeTypeAsCallable] @@ -803,11 +828,15 @@ S = TypeVar('S') def k1(x: int, y: List[T]) -> List[Union[T, int]]: pass def k2(x: S, y: List[T]) -> List[Union[T, int]]: pass a = k2 -a = k2 -a = k1 # E: Incompatible types in assignment (expression has type "Callable[[int, List[T]], List[Union[T, int]]]", variable has type "Callable[[S, List[T]], List[Union[T, int]]]") +if int(): + a = k2 +if int(): + a = k1 # E: Incompatible types in assignment (expression has type "Callable[[int, List[T]], List[Union[T, int]]]", variable has type "Callable[[S, List[T]], List[Union[T, int]]]") b = k1 -b = k1 -b = k2 +if int(): + b = k1 +if int(): + b = k2 [builtins fixtures/list.pyi] [case testAmbiguousUnionContextAndMultipleInheritance] @@ -851,8 +880,10 @@ def d_ab() -> Dict[A, B]: return {} def d_aa() -> Dict[A, A]: return {} a, b = None, None # type: (A, B) d = {a:b} -d = d_ab() -d = d_aa() # E: Incompatible types in assignment (expression has type "Dict[A, A]", variable has type "Dict[A, B]") +if int(): + d = d_ab() +if int(): + d = d_aa() # E: Incompatible types in assignment (expression has type "Dict[A, A]", variable has type "Dict[A, B]") [builtins fixtures/dict.pyi] [case testSetLiteral] @@ -861,9 +892,12 @@ a, x = None, None # type: (int, Any) def s_i() -> Set[int]: return set() def s_s() -> Set[str]: return set() s = {a} -s = {x} -s = s_i() -s = s_s() # E: Incompatible types in assignment (expression has type "Set[str]", variable has type "Set[int]") +if int(): + s = {x} +if int(): + s = s_i() +if int(): + s = s_s() # E: Incompatible types in assignment (expression has type "Set[str]", variable has type "Set[int]") [builtins fixtures/set.pyi] [case testSetWithStarExpr] @@ -1001,10 +1035,11 @@ def f() -> None: pass import typing for a in [A()]: pass a = A() -a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") -for a in []: pass -a = A() -a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + for a in []: pass + a = A() + a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") class A: pass class B: pass [builtins fixtures/for.pyi] @@ -1013,12 +1048,12 @@ class B: pass def f() -> None: for a in [A()]: pass a = A() - if 1: + if int(): a = B() \ # E: Incompatible types in assignment (expression has type "B", variable has type "A") for a in []: pass # E: Need type annotation for 'a' a = A() - if 1: + if int(): a = B() \ # E: Incompatible types in assignment (expression has type "B", variable has type "A") class A: pass @@ -1032,42 +1067,51 @@ class B: pass [case testMultipleAssignmentWithPartialDefinition] - a = None # type: A -x, a = a, a -x = a -a = x -x = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") -a = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") +if int(): + x, a = a, a + if int(): + x = a + a = x + if int(): + x = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") + a = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") class A: pass [case testMultipleAssignmentWithPartialDefinition2] - a = None # type: A -a, x = [a, a] -x = a -a = x -x = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") -a = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") +if int(): + a, x = [a, a] + if int(): + x = a + a = x + if int(): + x = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") + a = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") class A: pass [builtins fixtures/for.pyi] [case testMultipleAssignmentWithPartialDefinition3] from typing import Any, cast a = None # type: A -x, a = cast(Any, a) -x = a -a = x -x = object() -a = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") +if int(): + x, a = cast(Any, a) + if int(): + x = a + a = x + if int(): + x = object() + a = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") class A: pass [case testInferGlobalDefinedInBlock] import typing if A: a = A() - a = A() - a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") + if int(): + a = A() + if int(): + a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") class A: pass class B: pass @@ -1218,8 +1262,9 @@ from typing import List a = None # type: List[A] o = None # type: List[object] a2 = a or [] -a = a2 -a2 = o # E: Incompatible types in assignment (expression has type "List[object]", variable has type "List[A]") +if int(): + a = a2 + a2 = o # E: Incompatible types in assignment (expression has type "List[object]", variable has type "List[A]") class A: pass [builtins fixtures/list.pyi] @@ -1257,9 +1302,12 @@ a = None # type: List[A] x1 = [A(), B()] x2 = [B(), A()] x3 = [B(), B()] -a = x1 -a = x2 -a = x3 # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") +if int(): + a = x1 +if int(): + a = x2 +if int(): + a = x3 # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") [builtins fixtures/list.pyi] [case testListWithDucktypeCompatibilityAndTransitivity] @@ -1273,9 +1321,12 @@ a = None # type: List[A] x1 = [A(), C()] x2 = [C(), A()] x3 = [B(), C()] -a = x1 -a = x2 -a = x3 # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") +if int(): + a = x1 +if int(): + a = x2 +if int(): + a = x3 # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") [builtins fixtures/list.pyi] @@ -2013,7 +2064,8 @@ T = TypeVar('T', bound=str) def f() -> Tuple[T]: ... x = None -(x,) = f() +if int(): + (x,) = f() [out] [case testNoCrashOnPartialVariable3] @@ -2418,27 +2470,27 @@ def f(): pass [file m.py] def f(): pass _ = f -_ = 0 # E: Incompatible types in assignment (expression has type "int", variable has type "Callable[[], Any]") +_ = 0 [file a.py] def foo() -> None: import _ _.f() - _ = 0 # E: Name '_' already defined (by an import) + _ = 0 # E: Incompatible types in assignment (expression has type "int", variable has type Module) [file b.py] def foo() -> None: import m as _ _.f() - _ = 0 # E: Name '_' already defined (by an import) + _ = 0 # E: Incompatible types in assignment (expression has type "int", variable has type Module) [file c.py] def foo() -> None: from m import _ - _() - _ = 0 # E: Incompatible types in assignment (expression has type "int", variable has type "Callable[[], Any]") + _() # E: "int" not callable + _ = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") [file d.py] def foo() -> None: from m import f as _ _() - _ = 0 # E: Name '_' already defined on line 1 + _ = 0 # E: Incompatible types in assignment (expression has type "int", variable has type "Callable[[], Any]") [builtins fixtures/module.pyi] [case testUnusedTargetNotClass] diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index 7490bdf89f6c..54cd6667b9d2 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -1,6 +1,7 @@ [case testForcedAssignment] x = 1 # type: object y = 1 +def f(): x, y # Prevent independent redefinition y = x # E: Incompatible types in assignment (expression has type "object", variable has type "int") x = 2 y = x @@ -108,11 +109,12 @@ class B(A): def foo(): pass x = A() -x = B() -x.z -x = foo() -x.z # E: "A" has no attribute "z" -x.y +if int(): + x = B() + x.z + x = foo() + x.z # E: "A" has no attribute "z" + x.y [case testSingleMultiAssignment-skip] x = 'a' @@ -203,6 +205,7 @@ class B(A): z = 1 x = A() +def f(): x # Prevent redefinition of x x = B() x.z try: @@ -243,6 +246,7 @@ class B(A): z = 1 x = A() +def f(): x # Prevent redefinition of x x = B() try: raise BaseException() @@ -302,6 +306,7 @@ class B(A): b = 1 x = A() +def f(): x # Prevent redefinition x = B() try: x = A() @@ -318,6 +323,7 @@ class B(A): b = 1 x = A() +def f(): x # Prevent redefinition x = B() try: x = A() @@ -335,6 +341,7 @@ class B(A): b = 1 x = A() +def f(): x # Prevent redefinition x = B() try: x = A() @@ -353,6 +360,7 @@ class B(A): while 2: x = A() + def f(): x # Prevents redefinition x = B() try: x = A() @@ -711,6 +719,7 @@ from typing import Union, List while bool(): x = None # type: Union[int, str, List[int]] + def f(): x # Prevent redefinition x = 1 if isinstance(x, int): x + 1 @@ -762,6 +771,7 @@ from typing import Union def foo() -> Union[int, str]: pass x = foo() +def f(): x # Prevent redefinition x + 1 # E: Unsupported operand types for + ("str" and "int") \ # N: Left operand is of type "Union[int, str]" x + 'a' # E: Unsupported operand types for + ("int" and "str") \ @@ -788,6 +798,7 @@ from typing import Union def foo() -> Union[int, str]: pass x = foo() +def f(): x # Prevent redefinition x + 1 # E: Unsupported operand types for + ("str" and "int") \ # N: Left operand is of type "Union[int, str]" x = 'a' @@ -807,6 +818,7 @@ from typing import Union def foo() -> Union[int, str]: pass x = foo() +def f(): x # Prevent redefinition x + 1 # E: Unsupported operand types for + ("str" and "int") \ # N: Left operand is of type "Union[int, str]" x = 'a' @@ -827,6 +839,7 @@ from typing import Union def foo() -> Union[int, str]: pass x = foo() +def f(): x # Prevent redefinition x = 1 while bool(): @@ -854,6 +867,7 @@ from typing import Union def foo() -> Union[int, str]: pass x = foo() +def f(): x # Prevent redefinition x = 1 while bool(): @@ -885,6 +899,7 @@ from typing import Union def foo() -> Union[int, str]: pass x = foo() +def f(): x # Prevent redefinition x = 1 for y in [1]: @@ -916,6 +931,7 @@ from typing import Union def foo() -> Union[int, str]: pass x = foo() +def f(): x # Prevent redefinition x = 1 for y in [1]: diff --git a/test-data/unit/check-lists.test b/test-data/unit/check-lists.test index c575f4b76da4..a62293a5d7c0 100644 --- a/test-data/unit/check-lists.test +++ b/test-data/unit/check-lists.test @@ -6,9 +6,12 @@ from typing import List a1, b1, c1 = None, None, None # type: (A, B, C) a2, b2, c2 = None, None, None # type: (A, B, C) -a1, [b1, c1] = a2, [b2, c2] -a1, [a1, [b1, c1]] = a2, [a2, [b2, c2]] -a1, [a1, [a1, b1]] = a1, [a1, [a1, c1]] # E: Incompatible types in assignment (expression has type "C", variable has type "B") +if int(): + a1, [b1, c1] = a2, [b2, c2] +if int(): + a1, [a1, [b1, c1]] = a2, [a2, [b2, c2]] +if int(): + a1, [a1, [a1, b1]] = a1, [a1, [a1, c1]] # E: Incompatible types in assignment (expression has type "C", variable has type "B") class A: pass class B: pass @@ -35,10 +38,14 @@ from typing import List a, b, c = None, None, None # type: (A, B, C) t = a, b -[a, b], c = t, c -[a, c], c = t, c # E: Incompatible types in assignment (expression has type "B", variable has type "C") -[a, a, a], c = t, c # E: Need more than 2 values to unpack (3 expected) -[a], c = t, c # E: Too many values to unpack (1 expected, 2 provided) +if int(): + [a, b], c = t, c +if int(): + [a, c], c = t, c # E: Incompatible types in assignment (expression has type "B", variable has type "C") +if int(): + [a, a, a], c = t, c # E: Need more than 2 values to unpack (3 expected) +if int(): + [a], c = t, c # E: Too many values to unpack (1 expected, 2 provided) class A: pass class B: pass diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 5cf8d5fdfdee..cb8e75a9fe11 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -179,10 +179,14 @@ x = object() import m i, s = None, None # type: (int, str) -i = m.x -i = m.y -s = m.x # E: Incompatible types in assignment (expression has type "int", variable has type "str") -s = m.y # E: Incompatible types in assignment (expression has type "int", variable has type "str") +if int(): + i = m.x +if int(): + i = m.y +if int(): + s = m.x # E: Incompatible types in assignment (expression has type "int", variable has type "str") +if int(): + s = m.y # E: Incompatible types in assignment (expression has type "int", variable has type "str") [file m.py] x = y = 1 [builtins fixtures/primitives.pyi] @@ -365,7 +369,8 @@ m.a.x = m.a.y # Error import typing from .b import A, B, x, y z = x -z = y # Error +if int(): + z = y # Error [file m/b.py] import typing class A: pass @@ -373,7 +378,7 @@ class B: pass x = A() y = B() [out] -tmp/m/a.py:4: error: Incompatible types in assignment (expression has type "B", variable has type "A") +tmp/m/a.py:5: error: Incompatible types in assignment (expression has type "B", variable has type "A") main:3: error: Incompatible types in assignment (expression has type "B", variable has type "A") [case testRelativeImports2] @@ -634,7 +639,8 @@ from m import f def g() -> None: global f f = None - f = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Callable[[], Any]") + if int(): + f = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Callable[[], Any]") [file m.py] def f(): pass [out] @@ -1687,14 +1693,18 @@ a = 3 import m, n, o x = m -x = n # E: Cannot assign multiple modules to name 'x' without explicit 'types.ModuleType' annotation -x = o # E: Cannot assign multiple modules to name 'x' without explicit 'types.ModuleType' annotation +if int(): + x = n # E: Cannot assign multiple modules to name 'x' without explicit 'types.ModuleType' annotation +if int(): + x = o # E: Cannot assign multiple modules to name 'x' without explicit 'types.ModuleType' annotation y = o -y, z = m, n # E: Cannot assign multiple modules to name 'y' without explicit 'types.ModuleType' annotation +if int(): + y, z = m, n # E: Cannot assign multiple modules to name 'y' without explicit 'types.ModuleType' annotation xx = m -xx = m +if int(): + xx = m reveal_type(xx.a) # E: Revealed type is 'builtins.str' [file m.py] @@ -1857,7 +1867,8 @@ import mod import othermod alias = mod.submod reveal_type(alias.whatever('/')) # E: Revealed type is 'builtins.str*' -alias = othermod # E: Cannot assign multiple modules to name 'alias' without explicit 'types.ModuleType' annotation +if int(): + alias = othermod # E: Cannot assign multiple modules to name 'alias' without explicit 'types.ModuleType' annotation [file mod.py] import submod [file submod.py] diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 3f59d1a3536f..dafee75a056d 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -148,7 +148,8 @@ s = n.a # type: str # E: Incompatible types in assignment (expression has type i = n.b # type: int # E: Incompatible types in assignment (expression has type "str", \ variable has type "int") x, y = n -x = y # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + x = y # E: Incompatible types in assignment (expression has type "str", variable has type "int") [case testNamedTupleWithTupleFieldNamesWithItemTypes] @@ -161,7 +162,8 @@ s = n.a # type: str # E: Incompatible types in assignment (expression has type i = n.b # type: int # E: Incompatible types in assignment (expression has type "str", \ variable has type "int") x, y = n -x = y # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + x = y # E: Incompatible types in assignment (expression has type "str", variable has type "int") [case testNamedTupleConstructorArgumentTypes] @@ -182,9 +184,12 @@ class X(N): x = X(1, 2) # E: Argument 2 to "X" has incompatible type "int"; expected "str" s = '' i = 0 -s = x.a # E: Incompatible types in assignment (expression has type "int", variable has type "str") -i, s = x -s, s = x # E: Incompatible types in assignment (expression has type "int", variable has type "str") +if int(): + s = x.a # E: Incompatible types in assignment (expression has type "int", variable has type "str") +if int(): + i, s = x +if int(): + s, s = x # E: Incompatible types in assignment (expression has type "int", variable has type "str") [case testNamedTupleAsBaseClass2] from typing import NamedTuple @@ -194,9 +199,12 @@ class X(NamedTuple('N', [('a', int), x = X(1, 2) # E: Argument 2 to "X" has incompatible type "int"; expected "str" s = '' i = 0 -s = x.a # E: Incompatible types in assignment (expression has type "int", variable has type "str") -i, s = x -s, s = x # E: Incompatible types in assignment (expression has type "int", variable has type "str") +if int(): + s = x.a # E: Incompatible types in assignment (expression has type "int", variable has type "str") +if int(): + i, s = x +if int(): + s, s = x # E: Incompatible types in assignment (expression has type "int", variable has type "str") [case testNamedTuplesTwoAsBaseClasses] @@ -256,13 +264,20 @@ class B(A): pass a = A(1, '') b = B(1, '') t = None # type: Tuple[int, str] -b = a # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a = t # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "A") -b = t # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "B") -t = a -t = (1, '') -t = b -a = b +if int(): + b = a # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + a = t # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "A") +if int(): + b = t # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "B") +if int(): + t = a +if int(): + t = (1, '') +if int(): + t = b +if int(): + a = b [case testNamedTupleSimpleTypeInference] @@ -270,12 +285,16 @@ from typing import NamedTuple, Tuple A = NamedTuple('A', [('a', int)]) l = [A(1), A(2)] a = A(1) -a = l[0] +if int(): + a = l[0] (i,) = l[0] -i, i = l[0] # E: Need more than 1 value to unpack (2 expected) -l = [A(1)] -a = (1,) # E: Incompatible types in assignment (expression has type "Tuple[int]", \ - variable has type "A") +if int(): + i, i = l[0] # E: Need more than 1 value to unpack (2 expected) +if int(): + l = [A(1)] +if int(): + a = (1,) # E: Incompatible types in assignment (expression has type "Tuple[int]", \ + variable has type "A") [builtins fixtures/list.pyi] [case testNamedTupleMissingClassAttribute] @@ -418,7 +437,8 @@ def f(x: A) -> None: pass class B(NamedTuple('B', []), A): pass f(B()) x = None # type: A -x = B() +if int(): + x = B() # Sanity check: fail if baseclass does not match class C: pass @@ -427,7 +447,8 @@ class D(NamedTuple('D', []), A): pass g(D()) # E: Argument 1 to "g" has incompatible type "D"; expected "C" y = None # type: C -y = D() # E: Incompatible types in assignment (expression has type "D", variable has type "C") +if int(): + y = D() # E: Incompatible types in assignment (expression has type "D", variable has type "C") [case testNamedTupleSelfTypeMethod] from typing import TypeVar, NamedTuple diff --git a/test-data/unit/check-newtype.test b/test-data/unit/check-newtype.test index 25485f627d97..7a6e7aade2a0 100644 --- a/test-data/unit/check-newtype.test +++ b/test-data/unit/check-newtype.test @@ -309,17 +309,15 @@ main:4: error: Invalid type "__main__.T" from typing import NewType a = 3 -a = NewType('a', int) +def f(): a +a = NewType('a', int) # E: Cannot redefine 'a' as a NewType b = NewType('b', int) -b = NewType('b', float) # this line throws two errors +def g(): b +b = NewType('b', float) # E: Cannot assign to a type \ + # E: Cannot redefine 'b' as a NewType -c = NewType('c', str) # type: str -[out] -main:4: error: Cannot redefine 'a' as a NewType -main:7: error: Cannot assign to a type -main:7: error: Cannot redefine 'b' as a NewType -main:9: error: Cannot declare the type of a NewType declaration +c = NewType('c', str) # type: str # E: Cannot declare the type of a NewType declaration [case testNewTypeAddingExplicitTypesFails] from typing import NewType diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index a682b56056e5..82c6942bfa01 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -548,8 +548,10 @@ def f(x: T) -> ONode[T]: return None x = None # type: ONode[int] -x = f(1) -x = f('x') # E: Argument 1 to "f" has incompatible type "str"; expected "int" +if int(): + x = f(1) +if int(): + x = f('x') # E: Argument 1 to "f" has incompatible type "str"; expected "int" x.x = 1 # E: Item "None" of "Optional[Node[int]]" has no attribute "x" if x is not None: diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 8924ffd30016..c4e3657863db 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -455,10 +455,14 @@ from foo import * [file foo.pyi] from typing import overload a, b = None, None # type: (A, B) -b = f(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a = f(b) # E: Incompatible types in assignment (expression has type "B", variable has type "A") -a = f(a) -b = f(b) +if int(): + b = f(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + a = f(b) # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + a = f(a) +if int(): + b = f(b) @overload def f(x: 'A') -> 'A': pass @@ -492,10 +496,14 @@ from foo import * [file foo.pyi] from typing import overload a, b = None, None # type: (A, B) -b = a.f(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a = a.f(b) # E: Incompatible types in assignment (expression has type "B", variable has type "A") -a = a.f(a) -b = a.f(b) +if int(): + b = a.f(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + a = a.f(b) # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + a = a.f(a) +if int(): + b = a.f(b) class A: @overload @@ -509,14 +517,18 @@ from foo import * [file foo.pyi] from typing import overload a, b = None, None # type: (A, B) -a = f(a) -b = f(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + a = f(a) +if int(): + b = f(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") f(b) # E: No overload variant of "f" matches argument type "B" \ # N: Possible overload variant: \ # N: def f(x: A) -> A \ # N: <1 more non-matching overload not shown> -b = f(b, a) -a = f(b, a) # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + b = f(b, a) +if int(): + a = f(b, a) # E: Incompatible types in assignment (expression has type "B", variable has type "A") f(a, a) # E: No overload variant of "f" matches argument types "A", "A" \ # N: Possible overload variant: \ # N: def f(x: B, y: A) -> B \ @@ -539,11 +551,12 @@ from foo import * from typing import overload, TypeVar, Generic t = TypeVar('t') ab, ac, b, c = None, None, None, None # type: (A[B], A[C], B, C) -b = f(ab) -c = f(ac) -b = f(ac) # E: Incompatible types in assignment (expression has type "C", variable has type "B") -b = f(b) -c = f(b) # E: Incompatible types in assignment (expression has type "B", variable has type "C") +if int(): + b = f(ab) + c = f(ac) + b = f(ac) # E: Incompatible types in assignment (expression has type "C", variable has type "B") + b = f(b) + c = f(b) # E: Incompatible types in assignment (expression has type "B", variable has type "C") @overload def f(x: 'A[t]') -> t: pass @overload @@ -578,8 +591,10 @@ from typing import overload, Callable o = None # type: object a = None # type: A -a = f # E: Incompatible types in assignment (expression has type overloaded function, variable has type "A") -o = f +if int(): + a = f # E: Incompatible types in assignment (expression has type overloaded function, variable has type "A") +if int(): + o = f @overload def f(a: 'A') -> None: pass @@ -593,8 +608,9 @@ from foo import * from typing import overload t, a = None, None # type: (type, A) -a = A # E: Incompatible types in assignment (expression has type "Type[A]", variable has type "A") -t = A +if int(): + a = A # E: Incompatible types in assignment (expression has type "Type[A]", variable has type "A") + t = A class A: @overload @@ -608,10 +624,14 @@ from foo import * [file foo.pyi] from typing import overload a, b = None, None # type: int, str -a = A()[a] -b = A()[a] # E: Incompatible types in assignment (expression has type "int", variable has type "str") -b = A()[b] -a = A()[b] # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + a = A()[a] +if int(): + b = A()[a] # E: Incompatible types in assignment (expression has type "int", variable has type "str") +if int(): + b = A()[b] +if int(): + a = A()[b] # E: Incompatible types in assignment (expression has type "str", variable has type "int") class A: @overload @@ -625,10 +645,12 @@ from foo import * from typing import TypeVar, Generic, overload t = TypeVar('t') a, b, c = None, None, None # type: (A, B, C[A]) -a = c[a] -b = c[a] # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a = c[b] -b = c[b] # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + a = c[a] + b = c[a] # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + a = c[b] + b = c[b] # E: Incompatible types in assignment (expression has type "A", variable has type "B") class C(Generic[t]): @overload @@ -736,10 +758,14 @@ def f(t: type) -> 'A': pass @overload def f(t: 'A') -> 'B': pass a, b = None, None # type: (A, B) -a = f(A) -b = f(a) -b = f(A) # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a = f(a) # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + a = f(A) +if int(): + b = f(a) +if int(): + b = f(A) # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + a = f(a) # E: Incompatible types in assignment (expression has type "B", variable has type "A") class A: @overload def __init__(self) -> None: pass @@ -761,6 +787,7 @@ list_str = [] # type: List[str] list_object = [] # type: List[object] n = f(list_int) m = f(list_str) +def p(): n, m # Prevent redefinition n = 1 m = 1 n = 'x' # E: Incompatible types in assignment (expression has type "str", variable has type "int") diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 689ec46a83cb..6d8ed86ad758 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -150,12 +150,13 @@ c2: C2 y: AnotherP x = c -x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "P") -x = c1 # E: Incompatible types in assignment (expression has type "C1", variable has type "P") \ - # N: 'C1' is missing following 'P' protocol member: \ - # N: meth2 -x = c2 -x = y +if int(): + x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "P") + x = c1 # E: Incompatible types in assignment (expression has type "C1", variable has type "P") \ + # N: 'C1' is missing following 'P' protocol member: \ + # N: meth2 + x = c2 + x = y y = x [case testSimpleProtocolTwoMethodsExtend] @@ -183,9 +184,10 @@ reveal_type(x.meth1()) # E: Revealed type is 'builtins.int' reveal_type(x.meth2()) # E: Revealed type is 'builtins.str' x = C() # OK -x = Cbad() # E: Incompatible types in assignment (expression has type "Cbad", variable has type "P2") \ - # N: 'Cbad' is missing following 'P2' protocol member: \ - # N: meth2 +if int(): + x = Cbad() # E: Incompatible types in assignment (expression has type "Cbad", variable has type "P2") \ + # N: 'Cbad' is missing following 'P2' protocol member: \ + # N: meth2 [case testProtocolMethodVsAttributeErrors] from typing import Protocol @@ -687,12 +689,12 @@ class Bad: pass def f(s: Shape) -> None: pass -s: Shape - f(NonProtoShape()) f(Circle()) -s = Triangle() -s = Bad() +s: Shape +if int(): + s = Triangle() + s = Bad() n2: NonProtoShape = s [out] @@ -766,7 +768,8 @@ class D(Generic[T]): t: Traversable t = D[int]() # OK -t = C() # E: Incompatible types in assignment (expression has type "C", variable has type "Traversable") +if int(): + t = C() # E: Incompatible types in assignment (expression has type "C", variable has type "Traversable") [builtins fixtures/list.pyi] [typing fixtures/typing-full.pyi] @@ -821,8 +824,9 @@ class B: t: P1 t = A() # OK -t = B() # E: Incompatible types in assignment (expression has type "B", variable has type "P1") -t = C() # E: Incompatible types in assignment (expression has type "C", variable has type "P1") +if int(): + t = B() # E: Incompatible types in assignment (expression has type "B", variable has type "P1") + t = C() # E: Incompatible types in assignment (expression has type "C", variable has type "P1") [builtins fixtures/list.pyi] [typing fixtures/typing-full.pyi] @@ -955,10 +959,12 @@ x: PInst y: PClass x = CInst() -x = CClass() # E: Incompatible types in assignment (expression has type "CClass", variable has type "PInst") \ +if int(): + x = CClass() # E: Incompatible types in assignment (expression has type "CClass", variable has type "PInst") \ # N: Protocol member PInst.v expected instance variable, got class variable y = CClass() -y = CInst() # E: Incompatible types in assignment (expression has type "CInst", variable has type "PClass") \ +if int(): + y = CInst() # E: Incompatible types in assignment (expression has type "CInst", variable has type "PClass") \ # N: Protocol member PClass.v expected class variable, got instance variable [case testPropertyInProtocols] @@ -1042,12 +1048,15 @@ class C: x: P x = C() -x = B() +if int(): + x = B() y: PC y = B() -y = C() # E: Incompatible types in assignment (expression has type "C", variable has type "PC") \ - # N: Protocol member PC.meth expected class or static method +if int(): + y = C() \ + # E: Incompatible types in assignment (expression has type "C", variable has type "PC") \ + # N: Protocol member PC.meth expected class or static method [builtins fixtures/classmethod.pyi] [case testOverloadedMethodsInProtocols] @@ -1067,17 +1076,18 @@ class D: pass x: P = C() -x = D() +if int(): + x = D() [out] -main:17: error: Incompatible types in assignment (expression has type "D", variable has type "P") -main:17: note: Following member(s) of "D" have conflicts: -main:17: note: Expected: -main:17: note: @overload -main:17: note: def f(self, x: int) -> int -main:17: note: @overload -main:17: note: def f(self, x: str) -> str -main:17: note: Got: -main:17: note: def f(self, x: int) -> None +main:18: error: Incompatible types in assignment (expression has type "D", variable has type "P") +main:18: note: Following member(s) of "D" have conflicts: +main:18: note: Expected: +main:18: note: @overload +main:18: note: def f(self, x: int) -> int +main:18: note: @overload +main:18: note: def f(self, x: str) -> str +main:18: note: Got: +main:18: note: def f(self, x: int) -> None [case testCannotInstantiateProtocolWithOverloadedUnimplementedMethod] from typing import overload, Protocol @@ -1332,9 +1342,10 @@ class B: ... x: Union[P1, P2] x = C1() -x = C2() -x = C() -x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "Union[P1, P2]") +if int(): + x = C2() + x = C() + x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "Union[P1, P2]") [case testUnionsOfNormalClassesWithProtocols] from typing import Protocol, Union @@ -1430,15 +1441,17 @@ class C: var: Type[P] var() -var = P # E: Can only assign concrete classes to a variable of type "Type[P]" -var = B # OK -var = C # OK +if int(): + var = P # E: Can only assign concrete classes to a variable of type "Type[P]" + var = B # OK + var = C # OK var_old = None # type: Type[P] # Old syntax for variable annotations var_old() -var_old = P # E: Can only assign concrete classes to a variable of type "Type[P]" -var_old = B # OK -var_old = C # OK +if int(): + var_old = P # E: Can only assign concrete classes to a variable of type "Type[P]" + var_old = B # OK + var_old = C # OK [case testInstantiationProtocolInTypeForClassMethods] from typing import Type, Protocol diff --git a/test-data/unit/check-python2.test b/test-data/unit/check-python2.test index 5dddc149f812..e506c2559f2b 100644 --- a/test-data/unit/check-python2.test +++ b/test-data/unit/check-python2.test @@ -3,10 +3,14 @@ [case testUnicode] u = u'foo' -u = unicode() -s = '' -s = u'foo' # E: Incompatible types in assignment (expression has type "unicode", variable has type "str") -s = b'foo' +if int(): + u = unicode() +if int(): + s = '' +if int(): + s = u'foo' # E: Incompatible types in assignment (expression has type "unicode", variable has type "str") +if int(): + s = b'foo' [builtins_py2 fixtures/python2.pyi] [case testTypeVariableUnicode] @@ -34,8 +38,10 @@ class A: # type: (int) -> str pass s = A() / 1 -s = '' -s = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "str") +if int(): + s = '' +if int(): + s = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "str") [case testStrUnicodeCompatibility] import typing diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index 27f05f5ecec1..5103673e8984 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -31,17 +31,18 @@ def g(x: int) -> None: x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") reveal_type(x) # E: Revealed type is 'builtins.int' -[case testCannotRedefineAnnotationOnly] +[case testRedefineAnnotationOnly] def f() -> None: x: int x = '' \ # E: Incompatible types in assignment (expression has type "str", variable has type "int") reveal_type(x) # E: Revealed type is 'builtins.int' def g() -> None: - x: int # This syntax prevents redefinition through assignment + x: int x = 1 - x = '' \ - # E: Incompatible types in assignment (expression has type "str", variable has type "int") + reveal_type(x) # E: Revealed type is 'builtins.int' + x = '' + reveal_type(x) # E: Revealed type is 'builtins.str' [case testRedefineLocalUsingOldValue] from typing import TypeVar, Union @@ -216,7 +217,7 @@ def f() -> None: [case testCannotRedefineVarAsFunction] def f() -> None: def x(): pass - x = 1 # E: Name 'x' already defined on line 2 + x = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Callable[[], Any]") reveal_type(x) # E: Revealed type is 'def () -> Any' y = 1 def y(): pass # E: Name 'y' already defined on line 5 @@ -224,27 +225,116 @@ def f() -> None: [case testCannotRedefineVarAsClass] def f() -> None: class x: pass - x = 1 # E: Name 'x' already defined (possibly by an import) + x = 1 # E: Cannot assign to a type \ + # E: Incompatible types in assignment (expression has type "int", variable has type "Type[x]") y = 1 class y: pass # E: Name 'y' already defined on line 4 -[case testCannotRedefineVarAsTypeVar] +[case testRedefineVarAsTypeVar] from typing import TypeVar def f() -> None: + # Since these look like assignments, they can be redefined. x = TypeVar('x') - x = 1 # E: Name 'x' already defined on line 3 - def g(a: x) -> x: pass - reveal_type(g(1)) # E: Revealed type is 'builtins.int*' + x = 1 + reveal_type(x) # E: Revealed type is 'builtins.int' y = 1 - # This is arguably inconsistent -- we accept this since it looks like an assignment. y = TypeVar('y') - def h(a: y) -> y: return a - reveal_type(h('')) # E: Revealed type is 'builtins.str*' + # TODO: This should either work or we should give an error earlier. + def h(a: y) -> y: return a # E: Invalid type "y" [case testCannotRedefineVarAsModule] def f() -> None: import typing as m - m = 1 # E: Name 'm' already defined (by an import) + m = 1 # E: Incompatible types in assignment (expression has type "int", variable has type Module) n = 1 import typing as n # E: Name 'n' already defined on line 4 [builtins fixtures/module.pyi] + +[case testRedefineLocalWithTypeAnnotation] +def f() -> None: + x = 1 + x = '' # type: object + reveal_type(x) # E: Revealed type is 'builtins.object' +def g() -> None: + x = 1 + x: object = '' + reveal_type(x) # E: Revealed type is 'builtins.object' +def h() -> None: + x: int + x = 1 + x: object + x: object = '' # E: Name 'x' already defined on line 12 +def farg(x: int) -> None: + x: str = '' + +[case testRedefineLocalWithTypeAnnotationSpecialCases] +def f() -> None: + x: object + x = 1 + if int(): + x = '' + reveal_type(x) # E: Revealed type is 'builtins.object' + x = '' + reveal_type(x) # E: Revealed type is 'builtins.str' + if int(): + x = 2 \ + # E: Incompatible types in assignment (expression has type "int", variable has type "str") + + +-- Redefine global variable +-- ------------------------ + + +[case testRedefineGlobalWithDifferentType] +import m +reveal_type(m.x) +[file m.py] +x = 0 +reveal_type(x) +x = object() +reveal_type(x) +x = '' +reveal_type(x) +[out] +tmp/m.py:2: error: Revealed type is 'builtins.int' +tmp/m.py:4: error: Revealed type is 'builtins.object' +tmp/m.py:6: error: Revealed type is 'builtins.str' +main:2: error: Revealed type is 'builtins.str' + +[case testRedefineGlobalForIndex] +import m +reveal_type(m.x) +[file m.py] +from typing import Iterable +def f(): pass +it1: Iterable[int] = f() +it2: Iterable[str] = f() +for x in it1: + reveal_type(x) +for x in it2: + reveal_type(x) +reveal_type(x) +[out] +tmp/m.py:6: error: Revealed type is 'builtins.int*' +tmp/m.py:8: error: Revealed type is 'builtins.str*' +tmp/m.py:9: error: Revealed type is 'builtins.str*' +main:2: error: Revealed type is 'builtins.str*' + +[case testRedefineGlobalBasedOnPreviousValues] +from typing import TypeVar, Iterable +T = TypeVar('T') +def f(x: T) -> Iterable[T]: pass +a = 0 +a = f(a) +reveal_type(a) # E: Revealed type is 'typing.Iterable[builtins.int*]' + +[case testRedefineGlobalWithSeparateDeclaration] +x = '' +reveal_type(x) # E: Revealed type is 'builtins.str' +x: int +x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +x: object +x = 1 +reveal_type(x) # E: Revealed type is 'builtins.int' +if int(): + x = object() diff --git a/test-data/unit/check-serialize.test b/test-data/unit/check-serialize.test index 028dc6847d09..15074a929843 100644 --- a/test-data/unit/check-serialize.test +++ b/test-data/unit/check-serialize.test @@ -662,13 +662,14 @@ import b import b t: type t = b.A -t = b.f # E +if int(): + t = b.f # E [file b.py] class A: pass def f() -> None: pass [builtins fixtures/tuple.pyi] [out2] -tmp/a.py:4: error: Incompatible types in assignment (expression has type "Callable[[], None]", variable has type "type") +tmp/a.py:5: error: Incompatible types in assignment (expression has type "Callable[[], None]", variable has type "type") [case testSerializeOverloadedVsTypeObjectDistinction] import a @@ -678,7 +679,8 @@ import b import b t: type t = b.A -t = b.f # E +if int(): + t = b.f # E [file b.pyi] from typing import overload class A: @@ -692,7 +694,7 @@ def f() -> None: pass def f(x: int) -> None: pass [builtins fixtures/tuple.pyi] [out2] -tmp/a.py:4: error: Incompatible types in assignment (expression has type overloaded function, variable has type "type") +tmp/a.py:5: error: Incompatible types in assignment (expression has type overloaded function, variable has type "type") [case testSerializeNamedTupleInMethod4] from ntcrash import C diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index 7f9ff5f16e46..d57e79ac5ce8 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -471,32 +471,29 @@ class B: pass main:7: error: Incompatible types in assignment (expression has type "object", variable has type "BaseException") [case testTypeErrorInBlock] - while object: - x = None # type: A - x = object() - x = B() + x = None # type: A + if int(): + x = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") + x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") class A: pass class B: pass -[out] -main:4: error: Incompatible types in assignment (expression has type "object", variable has type "A") -main:5: error: Incompatible types in assignment (expression has type "B", variable has type "A") [case testTypeErrorInvolvingBaseException] x, a = None, None # type: (BaseException, A) -a = BaseException() # Fail -a = object() # Fail -x = object() # Fail -x = A() # Fail -x = BaseException() +if int(): + a = BaseException() # E: Incompatible types in assignment (expression has type "BaseException", variable has type "A") +if int(): + a = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") +if int(): + x = object() # E: Incompatible types in assignment (expression has type "object", variable has type "BaseException") +if int(): + x = A() # E: Incompatible types in assignment (expression has type "A", variable has type "BaseException") +if int(): + x = BaseException() class A: pass [builtins fixtures/exception.pyi] -[out] -main:3: error: Incompatible types in assignment (expression has type "BaseException", variable has type "A") -main:4: error: Incompatible types in assignment (expression has type "object", variable has type "A") -main:5: error: Incompatible types in assignment (expression has type "object", variable has type "BaseException") -main:6: error: Incompatible types in assignment (expression has type "A", variable has type "BaseException") [case testSimpleTryExcept2] import typing @@ -786,6 +783,7 @@ import typing class E1(BaseException): pass class E2(BaseException): pass e = 1 +def f(): e # Prevent redefinition e = 1 try: pass except E1 as e: pass @@ -1426,10 +1424,14 @@ import typing class A: pass class B: pass x = y = A() -x = A() -y = A() -x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") -y = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + x = A() +if int(): + y = A() +if int(): + x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + y = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") [case testChainedAssignment2] import typing @@ -1443,12 +1445,15 @@ def f() -> None: [out] [case testChainedAssignmentWithType] - x = y = None # type: int -x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") -y = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") -x = 1 -y = 1 +if int(): + x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + y = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + x = 1 +if int(): + y = 1 -- Star assignment @@ -1458,10 +1463,14 @@ y = 1 [case testAssignListToStarExpr] from typing import List bs, cs = None, None # type: List[A], List[B] -*bs, b = bs -*bs, c = cs # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") -*ns, c = cs -nc = cs +if int(): + *bs, b = bs +if int(): + *bs, c = cs # E: Incompatible types in assignment (expression has type "List[B]", variable has type "List[A]") + if int(): + *ns, c = cs +if int(): + nc = cs class A: pass class B: pass @@ -1548,13 +1557,14 @@ main:8: error: Incompatible types in assignment (expression has type "A", variab [case testAugmentedAssignmentIntFloat] weight0 = 65.5 reveal_type(weight0) # E: Revealed type is 'builtins.float' -weight0 = 65 -reveal_type(weight0) # E: Revealed type is 'builtins.int' -weight0 *= 'a' # E: Incompatible types in assignment (expression has type "str", variable has type "float") -weight0 *= 0.5 -reveal_type(weight0) # E: Revealed type is 'builtins.float' -weight0 *= object() # E: Unsupported operand types for * ("float" and "object") -reveal_type(weight0) # E: Revealed type is 'builtins.float' +if int(): + weight0 = 65 + reveal_type(weight0) # E: Revealed type is 'builtins.int' + weight0 *= 'a' # E: Incompatible types in assignment (expression has type "str", variable has type "float") + weight0 *= 0.5 + reveal_type(weight0) # E: Revealed type is 'builtins.float' + weight0 *= object() # E: Unsupported operand types for * ("float" and "object") + reveal_type(weight0) # E: Revealed type is 'builtins.float' [builtins fixtures/float.pyi] diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 7a1316059cd7..1fdb56587e62 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -10,18 +10,28 @@ t3 = None # type: Tuple[A, A] t4 = None # type: Tuple[A, B] t5 = None # type: Tuple[B, A] -t1 = t2 # E: Incompatible types in assignment (expression has type "Tuple[B]", variable has type "Tuple[A]") -t1 = t3 # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A]") -t3 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A]", variable has type "Tuple[A, A]") -t3 = t4 # E: Incompatible types in assignment (expression has type "Tuple[A, B]", variable has type "Tuple[A, A]") -t3 = t5 # E: Incompatible types in assignment (expression has type "Tuple[B, A]", variable has type "Tuple[A, A]") +if int(): + t1 = t2 # E: Incompatible types in assignment (expression has type "Tuple[B]", variable has type "Tuple[A]") +if int(): + t1 = t3 # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A]") +if int(): + t3 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A]", variable has type "Tuple[A, A]") +if int(): + t3 = t4 # E: Incompatible types in assignment (expression has type "Tuple[A, B]", variable has type "Tuple[A, A]") +if int(): + t3 = t5 # E: Incompatible types in assignment (expression has type "Tuple[B, A]", variable has type "Tuple[A, A]") # Ok -t1 = t1 -t2 = t2 -t3 = t3 -t4 = t4 -t5 = t5 +if int(): + t1 = t1 +if int(): + t2 = t2 +if int(): + t3 = t3 +if int(): + t4 = t4 +if int(): + t5 = t5 class A: pass class B: pass @@ -33,13 +43,14 @@ t1 = None # type: Tuple[A, A] t2 = None # type: Tuple[A, B] t3 = None # type: Tuple[B, A] -t2 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A, B]") -t2 = t3 # E: Incompatible types in assignment (expression has type "Tuple[B, A]", variable has type "Tuple[A, B]") -t3 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[B, A]") -t3 = t2 # E: Incompatible types in assignment (expression has type "Tuple[A, B]", variable has type "Tuple[B, A]") +if int(): + t2 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A, B]") + t2 = t3 # E: Incompatible types in assignment (expression has type "Tuple[B, A]", variable has type "Tuple[A, B]") + t3 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[B, A]") + t3 = t2 # E: Incompatible types in assignment (expression has type "Tuple[A, B]", variable has type "Tuple[B, A]") -t1 = t2 -t1 = t3 + t1 = t2 + t1 = t3 class A: pass class B(A): pass @@ -50,14 +61,19 @@ from typing import Tuple a, o = None, None # type: (A, object) t = None # type: Tuple[A, A] -a = t # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "A") -t = o # E: Incompatible types in assignment (expression has type "object", variable has type "Tuple[A, A]") -t = a # E: Incompatible types in assignment (expression has type "A", variable has type "Tuple[A, A]") +if int(): + a = t # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "A") +if int(): + t = o # E: Incompatible types in assignment (expression has type "object", variable has type "Tuple[A, A]") +if int(): + t = a # E: Incompatible types in assignment (expression has type "A", variable has type "Tuple[A, A]") # TODO: callable types + tuples # Ok -o = t -t = None +if int(): + o = t +if int(): + t = None class A: pass [builtins fixtures/tuple.pyi] @@ -67,8 +83,10 @@ from typing import Tuple t1 = None # type: Tuple[A, Tuple[A, A]] t2 = None # type: Tuple[B, Tuple[B, B]] -t2 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A, Tuple[A, A]]", variable has type "Tuple[B, Tuple[B, B]]") -t1 = t2 +if int(): + t2 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A, Tuple[A, A]]", variable has type "Tuple[B, Tuple[B, B]]") +if int(): + t1 = t2 class A: pass class B(A): pass @@ -79,8 +97,10 @@ from typing import Tuple t1 = None # type: Tuple[A, Tuple[A, A]] t2 = None # type: Tuple[B, Tuple[B, B]] -t2 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A, Tuple[A, A]]", variable has type "Tuple[B, Tuple[B, B]]") -t1 = t2 +if int(): + t2 = t1 # E: Incompatible types in assignment (expression has type "Tuple[A, Tuple[A, A]]", variable has type "Tuple[B, Tuple[B, B]]") +if int(): + t1 = t2 class A: pass class B(A): pass @@ -91,8 +111,10 @@ from typing import Tuple t1 = None # type: Tuple[A, A] t2 = None # type: tuple -t1 = t2 # E: Incompatible types in assignment (expression has type "Tuple[Any, ...]", variable has type "Tuple[A, A]") -t2 = t1 +if int(): + t1 = t2 # E: Incompatible types in assignment (expression has type "Tuple[Any, ...]", variable has type "Tuple[A, A]") +if int(): + t2 = t1 class A: pass [builtins fixtures/tuple.pyi] @@ -117,11 +139,16 @@ t3 = None # type: Tuple[A, B] a, b, c = None, None, None # type: (A, B, C) -t2 = () # E: Incompatible types in assignment (expression has type "Tuple[]", variable has type "Tuple[A]") -t2 = (a, a) # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A]") -t3 = (a, a) # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A, B]") -t3 = (b, b) # E: Incompatible types in assignment (expression has type "Tuple[B, B]", variable has type "Tuple[A, B]") -t3 = (a, b, a) # E: Incompatible types in assignment (expression has type "Tuple[A, B, A]", variable has type "Tuple[A, B]") +if int(): + t2 = () # E: Incompatible types in assignment (expression has type "Tuple[]", variable has type "Tuple[A]") +if int(): + t2 = (a, a) # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A]") +if int(): + t3 = (a, a) # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A, B]") +if int(): + t3 = (b, b) # E: Incompatible types in assignment (expression has type "Tuple[B, B]", variable has type "Tuple[A, B]") +if int(): + t3 = (a, b, a) # E: Incompatible types in assignment (expression has type "Tuple[A, B, A]", variable has type "Tuple[A, B]") t1 = () t1 = (a,) @@ -158,22 +185,32 @@ x = None # type: Tuple[A, B, C] y = None # type: Tuple[A, C, E] n = 0 -a = t1[1] # E: Incompatible types in assignment (expression has type "B", variable has type "A") -b = t1[0] # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + a = t1[1] # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + b = t1[0] # E: Incompatible types in assignment (expression has type "A", variable has type "B") t1[2] # E: Tuple index out of range t1[3] # E: Tuple index out of range t2[1] # E: Tuple index out of range reveal_type(t1[n]) # E: Revealed type is 'Union[__main__.A, __main__.B]' reveal_type(t3[n:]) # E: Revealed type is 'Union[__main__.A, __main__.B, __main__.C, __main__.D, __main__.E]' -b = t1[(0)] # E: Incompatible types in assignment (expression has type "A", variable has type "B") - -a = t1[0] -b = t1[1] -b = t1[-1] -a = t1[(0)] -x = t3[0:3] # type (A, B, C) -y = t3[0:5:2] # type (A, C, E) -x = t3[:-2] # type (A, B, C) +if int(): + b = t1[(0)] # E: Incompatible types in assignment (expression has type "A", variable has type "B") + +if int(): + a = t1[0] +if int(): + b = t1[1] +if int(): + b = t1[-1] +if int(): + a = t1[(0)] +if int(): + x = t3[0:3] # type (A, B, C) +if int(): + y = t3[0:5:2] # type (A, C, E) +if int(): + x = t3[:-2] # type (A, B, C) class A: pass class B: pass @@ -188,15 +225,21 @@ t1 = None # type: Tuple[A, B] t2 = None # type: Tuple[A] a, b = None, None # type: A, B -a = t1[-1] # E: Incompatible types in assignment (expression has type "B", variable has type "A") -b = t1[-2] # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + a = t1[-1] # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + b = t1[-2] # E: Incompatible types in assignment (expression has type "A", variable has type "B") t1[-3] # E: Tuple index out of range t1[-4] # E: Tuple index out of range -b = t2[(-1)] # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + b = t2[(-1)] # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a = t1[-2] -b = t1[-1] -a = t2[(-1)] +if int(): + a = t1[-2] +if int(): + b = t1[-1] +if int(): + a = t2[(-1)] class A: pass class B: pass @@ -230,12 +273,17 @@ a, b = None, None # type: (A, B) reveal_type(a1) # E: Revealed type is '__main__.A' reveal_type(b1) # E: Revealed type is '__main__.B' -a, a = t1 # E: Incompatible types in assignment (expression has type "B", variable has type "A") -b, b = t1 # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a, b, b = t2 # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + a, a = t1 # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + b, b = t1 # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + a, b, b = t2 # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a, b = t1 -a, b, a1 = t2 +if int(): + a, b = t1 +if int(): + a, b, a1 = t2 class A: pass class B: pass @@ -318,29 +366,34 @@ class A: pass a, b = None, None # type: (A, B) -a, b = a, a # Fail -a, b = b, a # Fail +if int(): + a, b = a, a # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + a, b = b, a \ + # E: Incompatible types in assignment (expression has type "B", variable has type "A") \ + # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a, b = a, b -a, a = a, a +if int(): + a, b = a, b +if int(): + a, a = a, a class A: pass class B: pass [builtins fixtures/tuple.pyi] -[out] -main:4: error: Incompatible types in assignment (expression has type "A", variable has type "B") -main:5: error: Incompatible types in assignment (expression has type "B", variable has type "A") -main:5: error: Incompatible types in assignment (expression has type "A", variable has type "B") [case testSubtypingInMultipleAssignment] - a, b = None, None # type: (A, B) -b, b = a, b # E: Incompatible types in assignment (expression has type "A", variable has type "B") -b, b = b, a # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + b, b = a, b # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + b, b = b, a # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a, b = b, b -b, a = b, b +if int(): + a, b = b, b +if int(): + b, a = b, b class A: pass class B(A): pass @@ -396,27 +449,37 @@ class BB: pass [builtins fixtures/tuple.pyi] [case testMultipleDeclarationWithParentheses] - (a, b) = (None, None) # type: int, str -a = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") -b = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "str") -a = 1 -b = '' +if int(): + a = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") + b = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "str") +if int(): + a = 1 + b = '' [case testMultipleAssignmentWithExtraParentheses] a, b = None, None # type: (A, B) -(a, b) = (a, a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") -(a, b) = (b, b) # E: Incompatible types in assignment (expression has type "B", variable has type "A") -((a), (b)) = ((a), (a)) # E: Incompatible types in assignment (expression has type "A", variable has type "B") -((a), (b)) = ((b), (b)) # E: Incompatible types in assignment (expression has type "B", variable has type "A") -[a, b] = a, a # E: Incompatible types in assignment (expression has type "A", variable has type "B") -[a, b] = b, b # E: Incompatible types in assignment (expression has type "B", variable has type "A") - -(a, b) = (a, b) -((a), (b)) = ((a), (b)) -[a, b] = a, b +if int(): + (a, b) = (a, a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + (a, b) = (b, b) # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + ((a), (b)) = ((a), (a)) # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + ((a), (b)) = ((b), (b)) # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + [a, b] = a, a # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + [a, b] = b, b # E: Incompatible types in assignment (expression has type "B", variable has type "A") + +if int(): + (a, b) = (a, b) +if int(): + ((a), (b)) = ((a), (b)) +if int(): + [a, b] = a, b class A: pass class B: pass @@ -425,10 +488,14 @@ class B: pass [case testMultipleAssignmentUsingSingleTupleType] from typing import Tuple a, b = None, None # type: Tuple[int, str] -a = 1 -b = '' -a = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") -b = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "str") +if int(): + a = 1 +if int(): + b = '' +if int(): + a = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + b = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "str") [case testMultipleAssignmentWithMixedVariables] a = b, c = 1, 1 @@ -452,18 +519,24 @@ aa, bb, *cc = t # E: Need type annotation for 'cc' from typing import List li, lo = None, None # type: List[int], List[object] a, b, *c = 1, 2 # type: int, int, List[int] -c = lo # E: Incompatible types in assignment (expression has type "List[object]", variable has type "List[int]") -c = li +if int(): + c = lo # E: Incompatible types in assignment (expression has type "List[object]", variable has type "List[int]") +if int(): + c = li [builtins fixtures/list.pyi] [case testAssignmentToStarCount1] from typing import List ca = None # type: List[int] c = [1] -a, b, *c = 1, # E: Need more than 1 value to unpack (2 expected) -a, b, *c = 1, 2 -a, b, *c = 1, 2, 3 -a, b, *c = 1, 2, 3, 4 +if int(): + a, b, *c = 1, # E: Need more than 1 value to unpack (2 expected) +if int(): + a, b, *c = 1, 2 +if int(): + a, b, *c = 1, 2, 3 +if int(): + a, b, *c = 1, 2, 3, 4 [builtins fixtures/list.pyi] [case testAssignmentToStarCount2] @@ -474,10 +547,14 @@ t2 = 1, 2 t3 = 1, 2, 3 t4 = 1, 2, 3, 4 c = [1] -a, b, *c = t1 # E: Need more than 1 value to unpack (2 expected) -a, b, *c = t2 -a, b, *c = t3 -a, b, *c = t4 +if int(): + a, b, *c = t1 # E: Need more than 1 value to unpack (2 expected) +if int(): + a, b, *c = t2 +if int(): + a, b, *c = t3 +if int(): + a, b, *c = t4 [builtins fixtures/list.pyi] [case testAssignmentToStarFromAny] @@ -492,10 +569,13 @@ class C: pass [case testAssignmentToComplexStar] from typing import List li = None # type: List[int] -a, *(li) = 1, +if int(): + a, *(li) = 1, a, *(b, c) = 1, 2 # E: Need more than 1 value to unpack (2 expected) -a, *(b, c) = 1, 2, 3 -a, *(b, c) = 1, 2, 3, 4 # E: Too many values to unpack (2 expected, 3 provided) +if int(): + a, *(b, c) = 1, 2, 3 +if int(): + a, *(b, c) = 1, 2, 3, 4 # E: Too many values to unpack (2 expected, 3 provided) [builtins fixtures/list.pyi] [case testAssignmentToStarFromTupleType] @@ -503,26 +583,29 @@ from typing import List, Tuple li = None # type: List[int] la = None # type: List[A] ta = None # type: Tuple[A, A, A] -a, *la = ta -a, *li = ta # E -a, *na = ta -na = la -na = a # E +if int(): + a, *la = ta +if int(): + a, *li = ta # E: List item 0 has incompatible type "A"; expected "int" \ + # E: List item 1 has incompatible type "A"; expected "int" +if int(): + a, *na = ta + if int(): + na = la + na = a # E: Incompatible types in assignment (expression has type "A", variable has type "List[A]") class A: pass [builtins fixtures/list.pyi] -[out] -main:6: error: List item 0 has incompatible type "A"; expected "int" -main:6: error: List item 1 has incompatible type "A"; expected "int" -main:9: error: Incompatible types in assignment (expression has type "A", variable has type "List[A]") [case testAssignmentToStarFromTupleInference] from typing import List li = None # type: List[int] la = None # type: List[A] a, *l = A(), A() -l = li # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]") -l = la +if int(): + l = li # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]") +if int(): + l = la class A: pass [builtins fixtures/list.pyi] @@ -533,8 +616,10 @@ from typing import List li = None # type: List[int] la = None # type: List[A] a, *l = [A(), A()] -l = li # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]") -l = la +if int(): + l = li # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]") +if int(): + l = la class A: pass [builtins fixtures/list.pyi] @@ -546,8 +631,10 @@ li = None # type: List[int] la = None # type: List[A] ta = None # type: Tuple[A, A, A] a, *l = ta -l = li # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]") -l = la +if int(): + l = li # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]") +if int(): + l = la class A: pass [builtins fixtures/list.pyi] @@ -558,8 +645,10 @@ from typing import List li = None # type: List[int] la = None # type: List[A] a, *l = la -l = li # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]") -l = la +if int(): + l = li # E: Incompatible types in assignment (expression has type "List[int]", variable has type "List[A]") +if int(): + l = la class A: pass [builtins fixtures/list.pyi] @@ -598,15 +687,16 @@ reveal_type(e2) # E: Revealed type is 'builtins.list[builtins.int]' a1, b1, c1 = None, None, None # type: (A, B, C) a2, b2, c2 = None, None, None # type: (A, B, C) -a1, (b1, c1) = a2, (b2, c2) -a1, (a1, (b1, c1)) = a2, (a2, (b2, c2)) -a1, (a1, (a1, b1)) = a1, (a1, (a1, c1)) # Fail +if int(): + a1, (b1, c1) = a2, (b2, c2) +if int(): + a1, (a1, (b1, c1)) = a2, (a2, (b2, c2)) +if int(): + a1, (a1, (a1, b1)) = a1, (a1, (a1, c1)) # E: Incompatible types in assignment (expression has type "C", variable has type "B") class A: pass class B: pass class C: pass -[out] -main:7: error: Incompatible types in assignment (expression has type "C", variable has type "B") [case testNestedTupleAssignment2] @@ -614,28 +704,30 @@ a1, b1, c1 = None, None, None # type: (A, B, C) a2, b2, c2 = None, None, None # type: (A, B, C) t = a1, b1 -a2, b2 = t -(a2, b2), c2 = t, c1 -(a2, c2), c2 = t, c1 # Fail -t, c2 = (a2, b2), c2 -t, c2 = (a2, a2), c2 # Fail -t = a1, a1, a1 # Fail -t = a1 # Fail -a2, a2, a2 = t # Fail -a2, = t # Fail -a2 = t # Fail +if int(): + a2, b2 = t +if int(): + (a2, b2), c2 = t, c1 +if int(): + (a2, c2), c2 = t, c1 # E: Incompatible types in assignment (expression has type "B", variable has type "C") +if int(): + t, c2 = (a2, b2), c2 +if int(): + t, c2 = (a2, a2), c2 # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A, B]") +if int(): + t = a1, a1, a1 # E: Incompatible types in assignment (expression has type "Tuple[A, A, A]", variable has type "Tuple[A, B]") +if int(): + t = a1 # E: Incompatible types in assignment (expression has type "A", variable has type "Tuple[A, B]") +if int(): + a2, a2, a2 = t # E: Need more than 2 values to unpack (3 expected) +if int(): + a2, = t # E: Too many values to unpack (1 expected, 2 provided) +if int(): + a2 = t # E: Incompatible types in assignment (expression has type "Tuple[A, B]", variable has type "A") class A: pass class B: pass class C: pass -[out] -main:8: error: Incompatible types in assignment (expression has type "B", variable has type "C") -main:10: error: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "Tuple[A, B]") -main:11: error: Incompatible types in assignment (expression has type "Tuple[A, A, A]", variable has type "Tuple[A, B]") -main:12: error: Incompatible types in assignment (expression has type "A", variable has type "Tuple[A, B]") -main:13: error: Need more than 2 values to unpack (3 expected) -main:14: error: Too many values to unpack (1 expected, 2 provided) -main:15: error: Incompatible types in assignment (expression has type "Tuple[A, B]", variable has type "A") -- Error messages @@ -680,14 +772,20 @@ i = 0 s = '' b = bool() -s = t.__len__() # E: Incompatible types in assignment (expression has type "int", variable has type "str") -i = t.__str__() # E: Incompatible types in assignment (expression has type "str", variable has type "int") -i = s in t # E: Incompatible types in assignment (expression has type "bool", variable has type "int") +if int(): + s = t.__len__() # E: Incompatible types in assignment (expression has type "int", variable has type "str") +if int(): + i = t.__str__() # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + i = s in t # E: Incompatible types in assignment (expression has type "bool", variable has type "int") t.foo # E: "Tuple[int, str]" has no attribute "foo" -i = t.__len__() -s = t.__str__() -b = s in t +if int(): + i = t.__len__() +if int(): + s = t.__str__() +if int(): + b = s in t [file builtins.py] from typing import TypeVar, Generic @@ -741,7 +839,8 @@ for x in B(), A(): [case testTupleIterable] y = 'a' x = sum((1,2)) -y = x # E: Incompatible types in assignment (expression has type "int", variable has type "str") +if int(): + y = x # E: Incompatible types in assignment (expression has type "int", variable has type "str") [builtins fixtures/tuple.pyi] @@ -970,12 +1069,14 @@ x, y = g(z) # E: Argument 1 to "g" has incompatible type "int"; expected "Tuple[ [case testTupleWithUndersizedContext] a = ([1], 'x') -a = ([], 'x', 1) # E: Incompatible types in assignment (expression has type "Tuple[List[int], str, int]", variable has type "Tuple[List[int], str]") +if int(): + a = ([], 'x', 1) # E: Incompatible types in assignment (expression has type "Tuple[List[int], str, int]", variable has type "Tuple[List[int], str]") [builtins fixtures/tuple.pyi] [case testTupleWithOversizedContext] a = (1, [1], 'x') -a = (1, []) # E: Incompatible types in assignment (expression has type "Tuple[int, List[int]]", variable has type "Tuple[int, List[int], str]") +if int(): + a = (1, []) # E: Incompatible types in assignment (expression has type "Tuple[int, List[int]]", variable has type "Tuple[int, List[int], str]") [builtins fixtures/tuple.pyi] [case testTupleWithoutContext] diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index b8ebe8105213..916709e7660f 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -58,15 +58,18 @@ U = Union[int, str] [case testProhibitReassigningAliases] A = float -A = int # E: Cannot assign multiple types to name "A" without an explicit "Type[...]" annotation +if int(): + A = int # E: Cannot assign multiple types to name "A" without an explicit "Type[...]" annotation [out] [case testProhibitReassigningSubscriptedAliases] from typing import Callable A = Callable[[], float] -A = Callable[[], int] # E: Cannot assign multiple types to name "A" without an explicit "Type[...]" annotation \ - # E: Value of type "int" is not indexable - # the second error is because of `Callable = 0` in lib-stub/typing.pyi +if int(): + A = Callable[[], int] \ + # E: Cannot assign multiple types to name "A" without an explicit "Type[...]" annotation \ + # E: Value of type "int" is not indexable + # the second error is because of `Callable = 0` in lib-stub/typing.pyi [builtins fixtures/list.pyi] [out] @@ -75,7 +78,8 @@ from typing import TypeVar, Union, Tuple T = TypeVar('T') A = Tuple[T, T] -A = Union[T, int] # E: Cannot assign multiple types to name "A" without an explicit "Type[...]" annotation \ +if int(): + A = Union[T, int] # E: Cannot assign multiple types to name "A" without an explicit "Type[...]" annotation \ # E: Value of type "int" is not indexable # the second error is because of `Union = 0` in lib-stub/typing.pyi [out] @@ -85,7 +89,8 @@ from typing import TypeVar, Sequence, Type T = TypeVar('T') A: Type[float] = int -A = float # OK +if int(): + A = float # OK x: A # E: Invalid type "__main__.A" def bad(tp: A) -> None: # E: Invalid type "__main__.A" pass diff --git a/test-data/unit/check-type-checks.test b/test-data/unit/check-type-checks.test index eead1ff968c0..483c8c5a3b79 100644 --- a/test-data/unit/check-type-checks.test +++ b/test-data/unit/check-type-checks.test @@ -2,15 +2,16 @@ [case testSimpleIsinstance] - x = None # type: object n = None # type: int s = None # type: str -n = x # E: Incompatible types in assignment (expression has type "object", variable has type "int") +if int(): + n = x # E: Incompatible types in assignment (expression has type "object", variable has type "int") if isinstance(x, int): n = x s = x # E: Incompatible types in assignment (expression has type "int", variable has type "str") -n = x # E: Incompatible types in assignment (expression has type "object", variable has type "int") +if int(): + n = x # E: Incompatible types in assignment (expression has type "object", variable has type "int") [builtins fixtures/isinstance.pyi] [case testSimpleIsinstance2] @@ -26,12 +27,12 @@ def f(x: object, n: int, s: str) -> None: [out] [case testSimpleIsinstance3] - class A: x = None # type: object n = None # type: int s = None # type: str - n = x # E: Incompatible types in assignment (expression has type "object", variable has type "int") + if int(): + n = x # E: Incompatible types in assignment (expression has type "object", variable has type "int") if isinstance(x, int): n = x s = x # E: Incompatible types in assignment (expression has type "int", variable has type "str") diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index 7ae6dbccafce..8a096b9f25e4 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -844,7 +844,8 @@ p2: Point p2 = dict(x='bye') # E: Key 'y' missing for TypedDict "Point" p3 = Point(x=1, y=2) -p3 = {'x': 'hi'} # E: Key 'y' missing for TypedDict "Point" +if int(): + p3 = {'x': 'hi'} # E: Key 'y' missing for TypedDict "Point" p4: Point = {'x': 1, 'y': 2} @@ -858,7 +859,8 @@ B = TypedDict('B', {'x': int, 'y': str}) T = TypeVar('T') def join(x: T, y: T) -> T: return x ab = join(A(x=1, y=1), B(x=1, y='')) -ab = {'x': 1, 'z': 1} # E: Expected TypedDict key 'x' but found keys ('x', 'z') +if int(): + ab = {'x': 1, 'z': 1} # E: Expected TypedDict key 'x' but found keys ('x', 'z') [builtins fixtures/dict.pyi] [case testCannotCreateAnonymousTypedDictInstanceUsingDictLiteralWithMissingItems] @@ -869,7 +871,8 @@ B = TypedDict('B', {'x': int, 'y': int, 'z': str}) T = TypeVar('T') def join(x: T, y: T) -> T: return x ab = join(A(x=1, y=1, z=1), B(x=1, y=1, z='')) -ab = {} # E: Expected TypedDict keys ('x', 'y') but found no keys +if int(): + ab = {} # E: Expected TypedDict keys ('x', 'y') but found no keys [builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-typevar-values.test b/test-data/unit/check-typevar-values.test index b8eae158e60f..d70d3c673966 100644 --- a/test-data/unit/check-typevar-values.test +++ b/test-data/unit/check-typevar-values.test @@ -16,9 +16,10 @@ def f(x: T) -> List[T]: pass i = [1] s = ['x'] o = [object()] -i = f(1) -s = f('') -o = f(1) # E: Value of type variable "T" of "f" cannot be "object" +if int(): + i = f(1) + s = f('') + o = f(1) # E: Value of type variable "T" of "f" cannot be "object" [builtins fixtures/list.pyi] [case testCallGenericFunctionWithTypeVarValueRestrictionAndAnyArgs] @@ -268,10 +269,12 @@ class C(Generic[X]): def __init__(self, x: X) -> None: pass x = None # type: C[str] y = C(S()) -x = y -y = x +if int(): + x = y + y = x c_int = C(1) # type: C[int] -y = c_int # E: Incompatible types in assignment (expression has type "C[int]", variable has type "C[str]") +if int(): + y = c_int # E: Incompatible types in assignment (expression has type "C[int]", variable has type "C[str]") [case testGenericTypeBodyWithTypevarValues] from typing import TypeVar, Generic @@ -523,11 +526,15 @@ U = TypeVar('U', str, A, int) def f(x: T) -> T: pass def g(x: U) -> U: pass a = f -a = f -a = g -b = g +if int(): + a = f +if int(): + a = g b = g -b = f # E: Incompatible types in assignment (expression has type "Callable[[T], T]", variable has type "Callable[[U], U]") +if int(): + b = g +if int(): + b = f # E: Incompatible types in assignment (expression has type "Callable[[T], T]", variable has type "Callable[[U], U]") [case testInnerFunctionWithTypevarValues] from typing import TypeVar diff --git a/test-data/unit/check-unions.test b/test-data/unit/check-unions.test index 75aea83de97e..79b473d98a3c 100644 --- a/test-data/unit/check-unions.test +++ b/test-data/unit/check-unions.test @@ -62,16 +62,20 @@ x = None # type: Union[A, C] y = None # type: int z = None # type: str -y = w.y +if int(): + y = w.y v.y # E: Item "C" of "Union[C, D]" has no attribute "y" \ # E: Item "D" of "Union[C, D]" has no attribute "y" u.y # E: Item "C" of "Union[A, C, D]" has no attribute "y" \ # E: Item "D" of "Union[A, C, D]" has no attribute "y" -z = w.y # E: Incompatible types in assignment (expression has type "int", variable has type "str") +if int(): + z = w.y # E: Incompatible types in assignment (expression has type "int", variable has type "str") w.y = 'a' # E: Incompatible types in assignment (expression has type "str", variable has type "int") -y = x.y # E: Item "C" of "Union[A, C]" has no attribute "y" +if int(): + y = x.y # E: Item "C" of "Union[A, C]" has no attribute "y" zz = x.y # E: Item "C" of "Union[A, C]" has no attribute "y" -z = zz # E: Incompatible types in assignment (expression has type "Union[int, Any]", variable has type "str") +if int(): + z = zz # E: Incompatible types in assignment (expression has type "Union[int, Any]", variable has type "str") [builtins fixtures/isinstance.pyi] @@ -92,7 +96,8 @@ i = None # type: int x.foo() y.foo() i = x.foo() -i = y.foo() # E: Incompatible types in assignment (expression has type "Union[int, str]", variable has type "int") +if int(): + i = y.foo() # E: Incompatible types in assignment (expression has type "Union[int, str]", variable has type "int") [builtins fixtures/isinstance.pyi] @@ -107,12 +112,17 @@ x[2] + 1 # E: Unsupported operand types for + ("str" and "int") \ [case testUnionAsOverloadArg] from foo import * x = 0 -x = f(1) -x = f('') +if int(): + x = f(1) +if int(): + x = f('') s = '' -s = f(int) -s = f(1) # E: Incompatible types in assignment (expression has type "int", variable has type "str") -x = f(int) # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + s = f(int) +if int(): + s = f(1) # E: Incompatible types in assignment (expression has type "int", variable has type "str") +if int(): + x = f(int) # E: Incompatible types in assignment (expression has type "str", variable has type "int") [file foo.pyi] from typing import Union, overload diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index 38483d0fa354..3682e2d53a9d 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -64,9 +64,10 @@ else: [file m.py] import typing x = 1 -x = 'a' +if int(): + x = 'a' [out] -tmp/m.py:3: error: Incompatible types in assignment (expression has type "str", variable has type "int") +tmp/m.py:4: error: Incompatible types in assignment (expression has type "str", variable has type "int") [case testNegatedMypyConditional] import typing diff --git a/test-data/unit/check-varargs.test b/test-data/unit/check-varargs.test index b161f5584e51..7146e2cd25b6 100644 --- a/test-data/unit/check-varargs.test +++ b/test-data/unit/check-varargs.test @@ -144,16 +144,25 @@ b = None # type: B c = None # type: C o = None # type: object -a = f(o) # E: Incompatible types in assignment (expression has type "object", variable has type "A") -b = f(b, a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") -b = f(a, b) # E: Incompatible types in assignment (expression has type "A", variable has type "B") - -o = f() -a = f(a) -a = f(b) -a = f(a, b, a) -o = f(a, b, o) -c = f(c) +if int(): + a = f(o) # E: Incompatible types in assignment (expression has type "object", variable has type "A") +if int(): + b = f(b, a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + b = f(a, b) # E: Incompatible types in assignment (expression has type "A", variable has type "B") + +if int(): + o = f() +if int(): + a = f(a) +if int(): + a = f(b) +if int(): + a = f(a, b, a) +if int(): + o = f(a, b, o) +if int(): + c = f(c) def f( *a: T) -> T: pass @@ -169,14 +178,21 @@ T = TypeVar('T') a = None # type: A o = None # type: object -a = f(o) # E: Incompatible types in assignment (expression has type "object", variable has type "A") -a = f(a, o) # E: Incompatible types in assignment (expression has type "object", variable has type "A") -a = f(a, a, o) # E: Incompatible types in assignment (expression has type "object", variable has type "A") -a = f(a, a, a, o) # E: Incompatible types in assignment (expression has type "object", variable has type "A") - -a = f(a) -a = f(a, a) -a = f(a, a, a) +if int(): + a = f(o) # E: Incompatible types in assignment (expression has type "object", variable has type "A") +if int(): + a = f(a, o) # E: Incompatible types in assignment (expression has type "object", variable has type "A") +if int(): + a = f(a, a, o) # E: Incompatible types in assignment (expression has type "object", variable has type "A") +if int(): + a = f(a, a, a, o) # E: Incompatible types in assignment (expression has type "object", variable has type "A") + +if int(): + a = f(a) +if int(): + a = f(a, a) +if int(): + a = f(a, a, a) def f(a: T, b: T = None, *c: T) -> T: pass @@ -427,18 +443,30 @@ from foo import * from typing import overload a, b = None, None # type: (A, B) -b = f() # E: Incompatible types in assignment (expression has type "A", variable has type "B") -b = f(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") -b = f(a, b) # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a = f(b) # E: Incompatible types in assignment (expression has type "B", variable has type "A") -a = f(b, b) # E: Incompatible types in assignment (expression has type "B", variable has type "A") -b = f(a, *[b]) # E: Incompatible types in assignment (expression has type "A", variable has type "B") -b = f(*()) # E: Incompatible types in assignment (expression has type "A", variable has type "B") -b = f(*(a,)) # E: Incompatible types in assignment (expression has type "A", variable has type "B") -b = f(*(a, b)) # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a = f(*(b,)) # E: Incompatible types in assignment (expression has type "B", variable has type "A") -a = f(*(b, b)) # E: Incompatible types in assignment (expression has type "B", variable has type "A") -a = f(*[b]) # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + b = f() # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + b = f(a) # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + b = f(a, b) # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + a = f(b) # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + a = f(b, b) # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + b = f(a, *[b]) # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + b = f(*()) # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + b = f(*(a,)) # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + b = f(*(a, b)) # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + a = f(*(b,)) # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + a = f(*(b, b)) # E: Incompatible types in assignment (expression has type "B", variable has type "A") +if int(): + a = f(*[b]) # E: Incompatible types in assignment (expression has type "B", variable has type "A") a = f() a = f(a) @@ -474,17 +502,27 @@ S = TypeVar('S') T = TypeVar('T') a, b, aa = None, None, None # type: (A, B, List[A]) -a, b = f(*aa) # Fail -b, b = f(*aa) # Fail -a, a = f(b, *aa) # Fail -b, b = f(b, *aa) # Fail -b, b = f(b, b, *aa) # Fail -a, b = f(a, *a) # Fail -a, b = f(*a) # Fail - -a, a = f(*aa) -b, a = f(b, *aa) -b, a = f(b, a, *aa) +if int(): + a, b = f(*aa) # E: Argument 1 to "f" has incompatible type "*List[A]"; expected "B" +if int(): + b, b = f(*aa) # E: Argument 1 to "f" has incompatible type "*List[A]"; expected "B" +if int(): + a, a = f(b, *aa) # E: Argument 1 to "f" has incompatible type "B"; expected "A" +if int(): + b, b = f(b, *aa) # E: Argument 2 to "f" has incompatible type "*List[A]"; expected "B" +if int(): + b, b = f(b, b, *aa) # E: Argument 3 to "f" has incompatible type "*List[A]"; expected "B" +if int(): + a, b = f(a, *a) # E: List or tuple expected as variable arguments +if int(): + a, b = f(*a) # E: List or tuple expected as variable arguments + +if int(): + a, a = f(*aa) +if int(): + b, a = f(b, *aa) +if int(): + b, a = f(b, a, *aa) def f(a: S, *b: T) -> Tuple[S, T]: pass @@ -492,14 +530,6 @@ def f(a: S, *b: T) -> Tuple[S, T]: class A: pass class B: pass [builtins fixtures/list.pyi] -[out] -main:6: error: Argument 1 to "f" has incompatible type "*List[A]"; expected "B" -main:7: error: Argument 1 to "f" has incompatible type "*List[A]"; expected "B" -main:8: error: Argument 1 to "f" has incompatible type "B"; expected "A" -main:9: error: Argument 2 to "f" has incompatible type "*List[A]"; expected "B" -main:10: error: Argument 3 to "f" has incompatible type "*List[A]"; expected "B" -main:11: error: List or tuple expected as variable arguments -main:12: error: List or tuple expected as variable arguments [case testCallerVarArgsTupleWithTypeInference] from typing import TypeVar, Tuple @@ -507,14 +537,20 @@ S = TypeVar('S') T = TypeVar('T') a, b = None, None # type: (A, B) -a, a = f(*(a, b)) # E: Argument 1 to "f" has incompatible type "*Tuple[A, B]"; expected "A" -b, b = f(a, *(b,)) # E: Argument 1 to "f" has incompatible type "A"; expected "B" -a, a = f(*(a, b)) # E: Argument 1 to "f" has incompatible type "*Tuple[A, B]"; expected "A" -b, b = f(a, *(b,)) # E: Argument 1 to "f" has incompatible type "A"; expected "B" -a, b = f(*(a, b, b)) # E: Too many arguments for "f" - -a, b = f(*(a, b)) -a, b = f(a, *(b,)) +if int(): + a, a = f(*(a, b)) # E: Argument 1 to "f" has incompatible type "*Tuple[A, B]"; expected "A" +if int(): + b, b = f(a, *(b,)) # E: Argument 1 to "f" has incompatible type "A"; expected "B" +if int(): + a, a = f(*(a, b)) # E: Argument 1 to "f" has incompatible type "*Tuple[A, B]"; expected "A" +if int(): + b, b = f(a, *(b,)) # E: Argument 1 to "f" has incompatible type "A"; expected "B" +if int(): + a, b = f(*(a, b, b)) # E: Too many arguments for "f" +if int(): + a, b = f(*(a, b)) +if int(): + a, b = f(a, *(b,)) def f(a: S, b: T) -> Tuple[S, T]: pass @@ -531,12 +567,21 @@ ao = None # type: List[object] aa = None # type: List[A] ab = None # type: List[B] -a, aa = G().f(*[a]) # Fail -aa, a = G().f(*[a]) # Fail -ab, aa = G().f(*[a]) # Fail - -ao, ao = G().f(*[a]) # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[object]") -aa, aa = G().f(*[a]) # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[A]") +if int(): + a, aa = G().f(*[a]) \ + # E: Incompatible types in assignment (expression has type "List[A]", variable has type "A") \ + # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[A]") +if int(): + aa, a = G().f(*[a]) # E: Incompatible types in assignment (expression has type "List[]", variable has type "A") +if int(): + ab, aa = G().f(*[a]) \ + # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[A]") \ + # E: Argument 1 to "f" of "G" has incompatible type "*List[A]"; expected "B" + +if int(): + ao, ao = G().f(*[a]) # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[object]") +if int(): + aa, aa = G().f(*[a]) # E: Incompatible types in assignment (expression has type "List[]", variable has type "List[A]") class G(Generic[T]): def f(self, *a: S) -> Tuple[List[S], List[T]]: @@ -545,12 +590,6 @@ class G(Generic[T]): class A: pass class B: pass [builtins fixtures/list.pyi] -[out] -main:9: error: Incompatible types in assignment (expression has type "List[A]", variable has type "A") -main:9: error: Incompatible types in assignment (expression has type "List[]", variable has type "List[A]") -main:10: error: Incompatible types in assignment (expression has type "List[]", variable has type "A") -main:11: error: Incompatible types in assignment (expression has type "List[]", variable has type "List[A]") -main:11: error: Argument 1 to "f" of "G" has incompatible type "*List[A]"; expected "B" -- Comment signatures @@ -626,7 +665,8 @@ f(a) # E: Argument 1 to "f" has incompatible type "List[int]"; expected "List[Un # N: Consider using "Sequence" instead, which is covariant x = [1] y = ['a'] -x = y # E: Incompatible types in assignment (expression has type "List[str]", variable has type "List[int]") +if int(): + x = y # E: Incompatible types in assignment (expression has type "List[str]", variable has type "List[int]") [builtins fixtures/list.pyi] [case testInvariantTypeConfusingNames] diff --git a/test-data/unit/check-warnings.test b/test-data/unit/check-warnings.test index d812ed83efa8..c3c8a824846b 100644 --- a/test-data/unit/check-warnings.test +++ b/test-data/unit/check-warnings.test @@ -42,9 +42,12 @@ c = add([cast(A, b)], [a]) [case testUnusedTypeIgnore] # flags: --warn-unused-ignores a = 1 -a = 'a' # type: ignore -a = 2 # type: ignore # N: unused 'type: ignore' comment -a = 'b' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +if int(): + a = 'a' # type: ignore +if int(): + a = 2 # type: ignore # N: unused 'type: ignore' comment +if int(): + a = 'b' # E: Incompatible types in assignment (expression has type "str", variable has type "int") [case testUnusedTypeIgnoreImport] # flags: --warn-unused-ignores diff --git a/test-data/unit/deps.test b/test-data/unit/deps.test index fdb16a87346d..02cf1c6db35e 100644 --- a/test-data/unit/deps.test +++ b/test-data/unit/deps.test @@ -1202,8 +1202,10 @@ def func() -> int: pass [out] -> m + -> m -> m - -> m, m.outer + -> , m, m.outer +--' [case testLogicalDefinitionMember] # flags: --logical-deps diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 372e4332e682..cb8ed361249d 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -4708,11 +4708,12 @@ x = '' from a import C c: C c = C.X -c = 1 +if int(): + c = 1 [out] == == -aa.py:4: error: Incompatible types in assignment (expression has type "int", variable has type "C") +aa.py:5: error: Incompatible types in assignment (expression has type "int", variable has type "C") [case testRefreshClassBasedIntEnum] import aa @@ -4734,14 +4735,16 @@ x = '' from a import C c: C c = C.X -c = 1 -n: int -n = C.X -n = c +if int(): + c = 1 + n: int + n = C.X + if int(): + n = c [out] == == -aa.py:4: error: Incompatible types in assignment (expression has type "int", variable has type "C") +aa.py:5: error: Incompatible types in assignment (expression has type "int", variable has type "C") [case testClassBasedEnumPropagation1] import a @@ -4805,11 +4808,12 @@ x = '' from a import C c: C c = C.X -c = 1 +if int(): + c = 1 [out] == == -aa.py:4: error: Incompatible types in assignment (expression has type "int", variable has type "C") +aa.py:5: error: Incompatible types in assignment (expression has type "int", variable has type "C") [case testRefreshFuncBasedIntEnum] import aa @@ -4830,14 +4834,15 @@ x = '' from a import C c: C c = C.X -c = 1 -n: int -n = C.X +if int(): + c = 1 # Error + n: int + n = C.X n = c [out] == == -aa.py:4: error: Incompatible types in assignment (expression has type "int", variable has type "C") +aa.py:5: error: Incompatible types in assignment (expression has type "int", variable has type "C") [case testFuncBasedEnumPropagation1] import a diff --git a/test-data/unit/semanal-statements.test b/test-data/unit/semanal-statements.test index 891113a3aa7a..c879dd5e342c 100644 --- a/test-data/unit/semanal-statements.test +++ b/test-data/unit/semanal-statements.test @@ -927,3 +927,60 @@ MypyFile:1( TupleExpr:5( NameExpr(a [l]) NameExpr(b [l])))))))) + +[case testRenameGlobalVariable] +def f(a): pass +x = 0 +f(x) +x = '' +f(x) +[out] +MypyFile:1( + FuncDef:1( + f + Args( + Var(a)) + Block:1( + PassStmt:1())) + AssignmentStmt:2( + NameExpr(x'* [__main__.x']) + IntExpr(0)) + ExpressionStmt:3( + CallExpr:3( + NameExpr(f [__main__.f]) + Args( + NameExpr(x' [__main__.x'])))) + AssignmentStmt:4( + NameExpr(x* [__main__.x]) + StrExpr()) + ExpressionStmt:5( + CallExpr:5( + NameExpr(f [__main__.f]) + Args( + NameExpr(x [__main__.x]))))) + +[case testRenameLocalVariable] +def f(a): + f(a) + a = '' + f(a) +[out] +MypyFile:1( + FuncDef:1( + f + Args( + Var(a)) + Block:1( + ExpressionStmt:2( + CallExpr:2( + NameExpr(f [__main__.f]) + Args( + NameExpr(a [l])))) + AssignmentStmt:3( + NameExpr(a'* [l]) + StrExpr()) + ExpressionStmt:4( + CallExpr:4( + NameExpr(f [__main__.f]) + Args( + NameExpr(a' [l]))))))) diff --git a/test-data/unit/typexport-basic.test b/test-data/unit/typexport-basic.test index 263be9837616..d56e35720149 100644 --- a/test-data/unit/typexport-basic.test +++ b/test-data/unit/typexport-basic.test @@ -204,16 +204,18 @@ CallExpr(5) : B from typing import Any a = None # type: A b = a # type: Any -b = a -a = b +if b: + b = a + a = b class A: pass [out] NameExpr(3) : A -NameExpr(4) : A NameExpr(4) : Any NameExpr(5) : A NameExpr(5) : Any +NameExpr(6) : A +NameExpr(6) : Any [case testMemberAssignment] from typing import Any @@ -851,7 +853,8 @@ from typing import List a = None # type: List[A] a or [] a = a or [] -a = [] or a +if int(): + a = [] or a class A: pass [builtins fixtures/list.pyi] [out] @@ -862,10 +865,12 @@ ListExpr(4) : builtins.list[A] NameExpr(4) : builtins.list[A] NameExpr(4) : builtins.list[A] OpExpr(4) : builtins.list[A] -ListExpr(5) : builtins.list[A] -NameExpr(5) : builtins.list[A] -NameExpr(5) : builtins.list[A] -OpExpr(5) : builtins.list[A] +CallExpr(5) : builtins.int +NameExpr(5) : def () -> builtins.int +ListExpr(6) : builtins.list[A] +NameExpr(6) : builtins.list[A] +NameExpr(6) : builtins.list[A] +OpExpr(6) : builtins.list[A] -- Class attributes From 9e2c6b8bae3886af3f4891789fa18ee029214f88 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 28 Sep 2018 12:48:13 +0100 Subject: [PATCH 10/47] Remove obsolete code --- mypy/semanal.py | 24 ++---------------------- mypy/semanal_pass1.py | 4 ---- 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 57c3c889403a..3ac3de5a9231 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -705,7 +705,6 @@ def analyze_function(self, defn: FuncItem) -> None: self.function_stack.append(defn) self.enter() for arg in defn.arguments: - #self.var_def_analyzer.process_assignment(arg.variable.name(), True) self.add_local(arg.variable, defn) # The first argument of a non-static, non-class method is like 'self' @@ -717,7 +716,7 @@ def analyze_function(self, defn: FuncItem) -> None: # First analyze body of the function but ignore nested functions. self.postpone_nested_functions_stack.append(FUNCTION_FIRST_PHASE_POSTPONE_SECOND) self.postponed_functions_stack.append([]) - self.visit_block(defn.body, new_var_scope=False) + self.visit_block(defn.body) # Analyze nested functions (if any) as a second phase. self.postpone_nested_functions_stack[-1] = FUNCTION_SECOND_PHASE @@ -1636,17 +1635,13 @@ def add_unknown_symbol(self, name: str, context: Context, is_import: bool = Fals # Statements # - def visit_block(self, b: Block, new_var_scope: bool = True) -> None: + def visit_block(self, b: Block) -> None: if b.is_unreachable: return self.block_depth[-1] += 1 - #if new_var_scope: - # self.var_def_analyzer.enter_block() for s in b.body: self.accept(s) self.block_depth[-1] -= 1 - #if new_var_scope: - # self.var_def_analyzer.leave_block() def visit_block_maybe(self, b: Optional[Block]) -> None: if b: @@ -2038,8 +2033,6 @@ def analyze_name_lvalue(self, else: self.msg.cant_assign_to_final(lval.name, self.type is not None, lval) - #is_new = self.var_def_analyzer.process_assignment(lval.name, True, not has_initializer) - # Top-level definitions within some statements (at least while) are # not handled in the first pass, so they have to be added now. nested_global = (not self.is_func_scope() and @@ -2068,7 +2061,6 @@ def analyze_name_lvalue(self, v = self.make_name_lvalue_var(lval, LDEF) self.add_local(v, lval) #, allow_redefine=is_new) if lval.name == '_': - 1 / 0 # Special case for assignment to local named '_': always infer 'Any'. typ = AnyType(TypeOfAny.special_form) self.store_declared_types(lval, typ) @@ -2669,9 +2661,7 @@ def visit_operator_assignment_stmt(self, def visit_while_stmt(self, s: WhileStmt) -> None: s.expr.accept(self) self.loop_depth += 1 - #self.var_def_analyzer.enter_loop() s.body.accept(self) - #self.var_def_analyzer.leave_loop() self.loop_depth -= 1 self.visit_block_maybe(s.else_body) @@ -2688,20 +2678,16 @@ def visit_for_stmt(self, s: ForStmt) -> None: self.store_declared_types(s.index, s.index_type) self.loop_depth += 1 - #self.var_def_analyzer.enter_loop() self.visit_block(s.body) - #self.var_def_analyzer.leave_loop() self.loop_depth -= 1 self.visit_block_maybe(s.else_body) def visit_break_stmt(self, s: BreakStmt) -> None: - #self.var_def_analyzer.reject_redefinition_of_vars_in_loop() if self.loop_depth == 0: self.fail("'break' outside loop", s, True, blocker=True) def visit_continue_stmt(self, s: ContinueStmt) -> None: - #self.var_def_analyzer.reject_redefinition_of_vars_in_loop() if self.loop_depth == 0: self.fail("'continue' outside loop", s, True, blocker=True) @@ -2717,9 +2703,7 @@ def visit_try_stmt(self, s: TryStmt) -> None: def analyze_try_stmt(self, s: TryStmt, visitor: NodeVisitor[None], add_global: bool = False) -> None: - #self.var_def_analyzer.enter_with_or_try() s.body.accept(visitor) - #self.var_def_analyzer.leave_with_or_try() for type, var, handler in zip(s.types, s.vars, s.handlers): if type: type.accept(visitor) @@ -2776,9 +2760,7 @@ def visit_with_stmt(self, s: WithStmt) -> None: elif isinstance(s.target_type, TupleType): s.target_type = s.target_type.copy_modified(items=new_types) - #self.var_def_analyzer.enter_with_or_try() self.visit_block(s.body) - #self.var_def_analyzer.leave_with_or_try() def visit_del_stmt(self, s: DelStmt) -> None: s.expr.accept(self) @@ -3578,8 +3560,6 @@ def add_symbol(self, name: str, node: SymbolTableNode, # TODO: Combine these methods in the first and second pass into a single one. if self.is_func_scope(): assert self.locals[-1] is not None - #is_new = (isinstance(node.node, Var) - # and self.var_def_analyzer.process_assignment(name, can_be_redefined=False)) if name in self.locals[-1]: # and not is_new: # Flag redefinition unless this is a reimport of a module. if not (node.kind == MODULE_REF and diff --git a/mypy/semanal_pass1.py b/mypy/semanal_pass1.py index 70e18d715ee3..6ea00e0d09f8 100644 --- a/mypy/semanal_pass1.py +++ b/mypy/semanal_pass1.py @@ -96,10 +96,8 @@ def visit_file(self, file: MypyFile, fnam: str, mod_id: str, options: Options) - v._fullname = self.sem.qualified_name(name) self.sem.globals[name] = SymbolTableNode(GDEF, v) - #self.sem.var_def_analyzer.enter_block() for d in defs: d.accept(self) - #self.sem.var_def_analyzer.leave_block() # Add implicit definition of literals/keywords to builtins, as we # cannot define a variable with them explicitly. @@ -144,10 +142,8 @@ def visit_block(self, b: Block) -> None: if b.is_unreachable: return self.sem.block_depth[-1] += 1 - #self.sem.var_def_analyzer.enter_block() for node in b.body: node.accept(self) - #self.sem.var_def_analyzer.leave_block() self.sem.block_depth[-1] -= 1 def visit_assignment_stmt(self, s: AssignmentStmt) -> None: From 2b885da6834def6a4806b6e7fb8895743966fd07 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 28 Sep 2018 13:00:28 +0100 Subject: [PATCH 11/47] Cleanup --- mypy/semanal.py | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 3ac3de5a9231..6c46186636c3 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2053,13 +2053,12 @@ def analyze_name_lvalue(self, self.fail("Cannot redefine an existing name as final", lval) assert lval.node.name() in self.globals or self.cur_mod_id == 'typing' elif (self.locals[-1] is not None - and (lval.name not in self.locals[-1]) - # or (is_new and not self.is_local_final(lval.name))) + and lval.name not in self.locals[-1] and lval.name not in self.global_decls[-1] and lval.name not in self.nonlocal_decls[-1]): # Define new local name. v = self.make_name_lvalue_var(lval, LDEF) - self.add_local(v, lval) #, allow_redefine=is_new) + self.add_local(v, lval) if lval.name == '_': # Special case for assignment to local named '_': always infer 'Any'. typ = AnyType(TypeOfAny.special_form) @@ -2075,23 +2074,6 @@ def analyze_name_lvalue(self, else: self.make_name_lvalue_point_to_existing_def(lval, explicit_type, is_final) - def get_original_def(self, name: str) -> Optional[SymbolTableNode]: - if self.is_func_scope(): - if not name.endswith("'"): - # Not a mangled name -- can't be an alias - return False - name = unmangle(name) - assert self.locals[-1] is not None, "No locals at function scope" - return self.locals[-1].get(name) - elif self.type is not None: - # TODO - return None - else: - orig_name = unmangle(name) + "'" - if name == orig_name: - return None - return self.globals.get(orig_name) - def is_alias_for_final_name(self, name: str) -> bool: if self.is_func_scope(): if not name.endswith("'"): @@ -3560,7 +3542,7 @@ def add_symbol(self, name: str, node: SymbolTableNode, # TODO: Combine these methods in the first and second pass into a single one. if self.is_func_scope(): assert self.locals[-1] is not None - if name in self.locals[-1]: # and not is_new: + if name in self.locals[-1]: # Flag redefinition unless this is a reimport of a module. if not (node.kind == MODULE_REF and self.locals[-1][name].node == node.node): From 712669c5debfdcd622c1ce7d3fbfe367736dc287 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 28 Sep 2018 13:54:28 +0100 Subject: [PATCH 12/47] Merge transform code into a single class --- mypy/semanal.py | 2 +- mypy/semanal_redef.py | 170 +++++++++++++++++++++++++++++++++-------- mypy/semanal_shared.py | 112 --------------------------- 3 files changed, 139 insertions(+), 145 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 6c46186636c3..9fd5ef7a9cd7 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -82,7 +82,7 @@ from mypy import experiments from mypy.plugin import Plugin, ClassDefContext, SemanticAnalyzerPluginInterface from mypy.util import get_prefix, correct_relative_import, unmangle -from mypy.semanal_shared import SemanticAnalyzerInterface, set_callable_name, VarDefAnalyzer +from mypy.semanal_shared import SemanticAnalyzerInterface, set_callable_name from mypy.scope import Scope from mypy.semanal_namedtuple import NamedTupleAnalyzer, NAMEDTUPLE_PROHIBITED_NAMES from mypy.semanal_typeddict import TypedDictAnalyzer diff --git a/mypy/semanal_redef.py b/mypy/semanal_redef.py index 1b17b51a41cb..e63e876d29f1 100644 --- a/mypy/semanal_redef.py +++ b/mypy/semanal_redef.py @@ -2,11 +2,10 @@ from mypy.nodes import ( Block, AssignmentStmt, NameExpr, MypyFile, FuncDef, Lvalue, ListExpr, TupleExpr, TempNode, - WhileStmt, ForStmt, BreakStmt, ContinueStmt, TryStmt, WithStmt, StarExpr, ImportFrom, MemberExpr, - IndexExpr + WhileStmt, ForStmt, BreakStmt, ContinueStmt, TryStmt, WithStmt, StarExpr, ImportFrom, + MemberExpr, IndexExpr ) from mypy.traverser import TraverserVisitor -from mypy.semanal_shared import VarDefAnalyzer class VariableRenameVisitor(TraverserVisitor): @@ -16,15 +15,22 @@ class VariableRenameVisitor(TraverserVisitor): x = 0 f(x) + x = '' g(x) - It can be renamed to this: + It would be transformed to this: + + x' = 0 + f(x') - x~ = 0 - f(x~) x = '' - g(X) + g(x) + + There will be two independent variables (x' and x) that will have separate + inferred types. + + Renaming only happens for assignments within the same block. TODO: * renaming in functions (argument redef) @@ -53,69 +59,82 @@ class VariableRenameVisitor(TraverserVisitor): """ def __init__(self) -> None: - self.var_def_analyzer = VarDefAnalyzer() + # Counter for labeling new blocks + self.block_id = 0 + self.disallow_redef_depth = 0 + self.loop_depth = 0 + # Map block id to loop depth. + self.block_loop_depth = {} # type: Dict[int, int] + # Stack of block ids being processed. + self.blocks = [] # type: List[int] + # List of scopes; each scope maps short name to block id. + self.var_blocks = [{}] # type: List[Dict[str, int]] + # Variables which have no assigned value yet (e.g., "x: t" but no assigment). + # Assignment in any block is considered an initialization. + self.uninitialized = set() # type: Set[str] + + # References to variables self.refs = [] # type: List[Dict[str, List[List[NameExpr]]]] def visit_mypy_file(self, file_node: MypyFile) -> None: - self.var_def_analyzer.clear() - self.var_def_analyzer.enter_block() + self.clear() + self.enter_block() self.refs.append({}) for d in file_node.defs: d.accept(self) self.flush_refs() - self.var_def_analyzer.leave_block() + self.leave_block() def visit_func_def(self, fdef: FuncDef) -> None: # Conservatively do not allow variable defined before a function to # be redefined later, since function could refer to either definition. - self.var_def_analyzer.reject_redefinition_of_vars_in_scope() - self.var_def_analyzer.process_assignment(fdef.name(), can_be_redefined=False) - self.var_def_analyzer.enter_scope() + self.reject_redefinition_of_vars_in_scope() + self.process_assignment(fdef.name(), can_be_redefined=False) + self.enter_scope() self.refs.append({}) for arg in fdef.arguments: name = arg.variable.name() - self.var_def_analyzer.process_assignment(arg.variable.name(), - can_be_redefined=True) + self.process_assignment(arg.variable.name(), can_be_redefined=True) self.handle_arg(name) self.visit_block(fdef.body, enter=False) self.flush_refs() - self.var_def_analyzer.leave_scope() + self.leave_scope() def visit_block(self, block: Block, enter: bool = True) -> None: if enter: - self.var_def_analyzer.enter_block() + self.enter_block() super().visit_block(block) if enter: - self.var_def_analyzer.leave_block() + self.leave_block() def visit_while_stmt(self, stmt: WhileStmt) -> None: - self.var_def_analyzer.enter_loop() + self.enter_loop() super().visit_while_stmt(stmt) - self.var_def_analyzer.leave_loop() + self.leave_loop() def visit_for_stmt(self, stmt: ForStmt) -> None: self.analyze_lvalue(stmt.index, True) - self.var_def_analyzer.enter_loop() + self.enter_loop() super().visit_for_stmt(stmt) - self.var_def_analyzer.leave_loop() + self.leave_loop() def visit_break_stmt(self, stmt: BreakStmt) -> None: - self.var_def_analyzer.reject_redefinition_of_vars_in_loop() + self.reject_redefinition_of_vars_in_loop() def visit_continue_stmt(self, stmt: ContinueStmt) -> None: - self.var_def_analyzer.reject_redefinition_of_vars_in_loop() + self.reject_redefinition_of_vars_in_loop() def visit_try_stmt(self, stmt: TryStmt) -> None: - self.var_def_analyzer.enter_with_or_try() + self.enter_with_or_try() super().visit_try_stmt(stmt) - self.var_def_analyzer.leave_with_or_try() + self.leave_with_or_try() def visit_with_stmt(self, stmt: WithStmt) -> None: - self.var_def_analyzer.enter_with_or_try() + self.enter_with_or_try() super().visit_with_stmt(stmt) - self.var_def_analyzer.leave_with_or_try() + self.leave_with_or_try() def visit_assignment_stmt(self, s: AssignmentStmt) -> None: has_initializer = not isinstance(s.rvalue, TempNode) @@ -126,7 +145,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: def analyze_lvalue(self, lvalue: Lvalue, has_initializer: bool) -> None: if isinstance(lvalue, NameExpr): name = lvalue.name - is_new = self.var_def_analyzer.process_assignment(name, True, not has_initializer) + is_new = self.process_assignment(name, True, not has_initializer) if is_new: # and name != '_': # Underscore gets special handling later self.handle_def(lvalue) else: @@ -144,7 +163,7 @@ def analyze_lvalue(self, lvalue: Lvalue, has_initializer: bool) -> None: def visit_import_from(self, imp: ImportFrom) -> None: for id, as_id in imp.names: - self.var_def_analyzer.process_assignment(as_id or id, False, False) + self.process_assignment(as_id or id, False, False) def visit_name_expr(self, expr: NameExpr) -> None: self.handle_ref(expr) @@ -166,7 +185,7 @@ def handle_ref(self, expr: NameExpr) -> None: names[-1].append(expr) def flush_refs(self) -> None: - is_func = self.var_def_analyzer.is_nested() + is_func = self.is_nested() for name, refs in self.refs[-1].items(): if len(refs) == 1: continue @@ -183,3 +202,90 @@ def rename_refs(self, names: List[NameExpr], index: int) -> None: new_name = name + "'" * (index + 1) for expr in names: expr.name = new_name + + # ---- + + def clear(self) -> None: + self.blocks = [] + self.var_blocks = [{}] + + def enter_block(self) -> None: + self.block_id += 1 + self.blocks.append(self.block_id) + self.block_loop_depth[self.block_id] = self.loop_depth + + def leave_block(self) -> None: + self.blocks.pop() + + def enter_with_or_try(self) -> None: + self.disallow_redef_depth += 1 + + def leave_with_or_try(self) -> None: + self.disallow_redef_depth -= 1 + + def enter_loop(self) -> None: + self.loop_depth += 1 + + def leave_loop(self) -> None: + self.loop_depth -= 1 + + def current_block(self) -> int: + return self.blocks[-1] + + def enter_scope(self) -> None: + self.var_blocks.append({}) + + def leave_scope(self) -> None: + self.var_blocks.pop() + + def is_nested(self) -> int: + return len(self.var_blocks) > 1 + + def reject_redefinition_of_vars_in_scope(self) -> None: + """Make it impossible to redefine defined variables in the current scope. + + This is used if we encounter a function definition or break/continue that + can make it ambiguous which definition is live. + """ + var_blocks = self.var_blocks[-1] + for key in var_blocks: + var_blocks[key] = -1 + + def reject_redefinition_of_vars_in_loop(self) -> None: + var_blocks = self.var_blocks[-1] + for key, block in var_blocks.items(): + if self.block_loop_depth.get(block) == self.loop_depth: + var_blocks[key] = -1 + + def process_assignment(self, name: str, can_be_redefined: bool, no_value: bool = False) -> bool: + """Record assignment to given name and return True if it defines a new name. + + Args: + can_be_redefined: If True, allows assignment in the same block to redefine the name + no_value: If True, the first assignment we encounter will not be considered to redefine + this but to initilize it (in any block) + """ + if self.disallow_redef_depth > 0: + can_be_redefined = False + block = self.current_block() + var_blocks = self.var_blocks[-1] + uninitialized = self.uninitialized + existing_no_value = name in self.uninitialized + if no_value: + uninitialized.add(name) + else: + uninitialized.discard(name) + if name not in var_blocks: + # New definition + if can_be_redefined: + var_blocks[name] = block + else: + # This doesn't support arbitrary redefinition. + # TODO: Make this less restricted. + var_blocks[name] = -1 + return True + elif var_blocks[name] == block and not existing_no_value: + # Redefinition + return True + else: + return False diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index 3af38d9bbb3e..b9a05765867f 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -150,115 +150,3 @@ def set_callable_name(sig: Type, fdef: FuncDef) -> Type: return sig.with_name(fdef.name()) else: return sig - - -class VarDefAnalyzer: - """Helper class that keeps track of variable redefinitions. - - This decides whether an assignment to an existing variable should define a new variable. - This can happen if there are multiple assignments to a variable within the same block: - - x = 0 - x = str(x) # Defines a new 'x' - - Since we now have two distinct 'x' variables, they can have independent inferred types. - """ - - def __init__(self) -> None: - self.block_id = 0 - self.disallow_redef_depth = 0 - self.loop_depth = 0 - # Map block id to loop depth. - self.block_loop_depth = {} # type: Dict[int, int] - # Stack of block ids being processed. - self.blocks = [] # type: List[int] - # List of scopes; each scope maps short name to block id. - self.var_blocks = [{}] # type: List[Dict[str, int]] - # Variables which have no assigned value yet (e.g., "x: t" but no assigment). - # Assignment in any block is considered an initialization. - self.uninitialized = set() # type: Set[str] - - def clear(self) -> None: - self.blocks = [] - self.var_blocks = [{}] - - def enter_block(self) -> None: - self.block_id += 1 - self.blocks.append(self.block_id) - self.block_loop_depth[self.block_id] = self.loop_depth - - def leave_block(self) -> None: - self.blocks.pop() - - def enter_with_or_try(self) -> None: - self.disallow_redef_depth += 1 - - def leave_with_or_try(self) -> None: - self.disallow_redef_depth -= 1 - - def enter_loop(self) -> None: - self.loop_depth += 1 - - def leave_loop(self) -> None: - self.loop_depth -= 1 - - def current_block(self) -> int: - return self.blocks[-1] - - def enter_scope(self) -> None: - self.var_blocks.append({}) - - def leave_scope(self) -> None: - self.var_blocks.pop() - - def is_nested(self) -> int: - return len(self.var_blocks) > 1 - - def reject_redefinition_of_vars_in_scope(self) -> None: - """Make it impossible to redefine defined variables in the current scope. - - This is used if we encounter a function definition or break/continue that - can make it ambiguous which definition is live. - """ - var_blocks = self.var_blocks[-1] - for key in var_blocks: - var_blocks[key] = -1 - - def reject_redefinition_of_vars_in_loop(self) -> None: - var_blocks = self.var_blocks[-1] - for key, block in var_blocks.items(): - if self.block_loop_depth.get(block) == self.loop_depth: - var_blocks[key] = -1 - - def process_assignment(self, name: str, can_be_redefined: bool, no_value: bool = False) -> bool: - """Record assignment to given name and return True if it defines a new name. - - Args: - can_be_redefined: If True, allows assignment in the same block to redefine the name - no_value: If True, the first assignment we encounter will not be considered to redefine - this but to initilize it (in any block) - """ - if self.disallow_redef_depth > 0: - can_be_redefined = False - block = self.current_block() - var_blocks = self.var_blocks[-1] - uninitialized = self.uninitialized - existing_no_value = name in self.uninitialized - if no_value: - uninitialized.add(name) - else: - uninitialized.discard(name) - if name not in var_blocks: - # New definition - if can_be_redefined: - var_blocks[name] = block - else: - # This doesn't support arbitrary redefinition. - # TODO: Make this less restricted. - var_blocks[name] = -1 - return True - elif var_blocks[name] == block and not existing_no_value: - # Redefinition - return True - else: - return False From e0b7df01706478bd5531b11956a29cb19ff957a3 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 28 Sep 2018 14:26:03 +0100 Subject: [PATCH 13/47] Code cleanup --- mypy/semanal_redef.py | 152 +++++++++++++++++++++++++----------------- 1 file changed, 91 insertions(+), 61 deletions(-) diff --git a/mypy/semanal_redef.py b/mypy/semanal_redef.py index e63e876d29f1..b6e3cc7bb3c9 100644 --- a/mypy/semanal_redef.py +++ b/mypy/semanal_redef.py @@ -16,46 +16,27 @@ class VariableRenameVisitor(TraverserVisitor): x = 0 f(x) - x = '' + x = "a" g(x) - It would be transformed to this: + It will be transformed like this: x' = 0 f(x') - x = '' + x = "a" g(x) There will be two independent variables (x' and x) that will have separate - inferred types. - - Renaming only happens for assignments within the same block. - - TODO: - * renaming in functions (argument redef) - * loops - * break/continue - * break/continue - * inititalizer / no initializer - * nested functions - * prevent redefinition -> no need to rename internally - * Final - - - multiple renamings - * global - - local - - - kinds of names - * for index variables - * imports - - funcdef and such - - classdef - - other ways of assigning to variables - - - nested class - - overloaded func - - decorated func + inferred types. The publicly exposed variant will get the non-suffixed name. + This is the last definition at module top level and the first definition + (argument) within a function. + + Renaming only happens for assignments within the same block. Renaming is + performed before semantic analysis, immediately after parsing. + + The implementation performs a rudimentary static analysis. The analysis is + overly conservative to keep things simple. """ def __init__(self) -> None: @@ -68,46 +49,53 @@ def __init__(self) -> None: # Stack of block ids being processed. self.blocks = [] # type: List[int] # List of scopes; each scope maps short name to block id. - self.var_blocks = [{}] # type: List[Dict[str, int]] + self.var_blocks = [] # type: List[Dict[str, int]] # Variables which have no assigned value yet (e.g., "x: t" but no assigment). # Assignment in any block is considered an initialization. self.uninitialized = set() # type: Set[str] - # References to variables + # References to variables the we may need to rename. List of + # scopes; each scope is a mapping from name to list of collections + # of names that refer to the same logical variable. self.refs = [] # type: List[Dict[str, List[List[NameExpr]]]] def visit_mypy_file(self, file_node: MypyFile) -> None: + """Rename variables within a file. + + This is the main entry point to this class. + """ self.clear() + self.enter_scope() self.enter_block() - self.refs.append({}) for d in file_node.defs: d.accept(self) - self.flush_refs() self.leave_block() + self.leave_scope() def visit_func_def(self, fdef: FuncDef) -> None: # Conservatively do not allow variable defined before a function to # be redefined later, since function could refer to either definition. self.reject_redefinition_of_vars_in_scope() - self.process_assignment(fdef.name(), can_be_redefined=False) + # Functions can't be redefined. + self.record_assignment(fdef.name(), can_be_redefined=False) + self.enter_scope() - self.refs.append({}) + self.enter_block() for arg in fdef.arguments: name = arg.variable.name() - self.process_assignment(arg.variable.name(), can_be_redefined=True) + self.record_assignment(arg.variable.name(), can_be_redefined=True) self.handle_arg(name) + for stmt in fdef.body.body: + stmt.accept(self) - self.visit_block(fdef.body, enter=False) - self.flush_refs() + self.leave_block() self.leave_scope() - def visit_block(self, block: Block, enter: bool = True) -> None: - if enter: - self.enter_block() + def visit_block(self, block: Block) -> None: + self.enter_block() super().visit_block(block) - if enter: - self.leave_block() + self.leave_block() def visit_while_stmt(self, stmt: WhileStmt) -> None: self.enter_loop() @@ -145,7 +133,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: def analyze_lvalue(self, lvalue: Lvalue, has_initializer: bool) -> None: if isinstance(lvalue, NameExpr): name = lvalue.name - is_new = self.process_assignment(name, True, not has_initializer) + is_new = self.record_assignment(name, True, not has_initializer) if is_new: # and name != '_': # Underscore gets special handling later self.handle_def(lvalue) else: @@ -163,20 +151,25 @@ def analyze_lvalue(self, lvalue: Lvalue, has_initializer: bool) -> None: def visit_import_from(self, imp: ImportFrom) -> None: for id, as_id in imp.names: - self.process_assignment(as_id or id, False, False) + self.record_assignment(as_id or id, False, False) def visit_name_expr(self, expr: NameExpr) -> None: self.handle_ref(expr) + # Helpers for renaming references + def handle_arg(self, name: str) -> None: + """Store function argument.""" if name not in self.refs[-1]: self.refs[-1][name] = [[]] def handle_def(self, expr: NameExpr) -> None: + """Store new name definition.""" names = self.refs[-1].setdefault(expr.name, []) names.append([expr]) def handle_ref(self, expr: NameExpr) -> None: + """Store reference to defined name.""" name = expr.name if name in self.refs[-1]: names = self.refs[-1][name] @@ -185,13 +178,22 @@ def handle_ref(self, expr: NameExpr) -> None: names[-1].append(expr) def flush_refs(self) -> None: + """Rename all references within the current scope. + + This will be called at the end of a scope. + """ is_func = self.is_nested() for name, refs in self.refs[-1].items(): if len(refs) == 1: + # Only one definition -- no renaming neeed. continue if is_func: + # In a function, don't rename the first definition, as it + # may be an argument that must preserve the name. to_rename = refs[1:] else: + # At module top level, don't rename the final definition, + # as it will be publicly visible outside the module. to_rename = refs[:-1] for i, item in enumerate(to_rename): self.rename_refs(item, i) @@ -203,11 +205,11 @@ def rename_refs(self, names: List[NameExpr], index: int) -> None: for expr in names: expr.name = new_name - # ---- + # Helpers for determining which assignments define new variables def clear(self) -> None: self.blocks = [] - self.var_blocks = [{}] + self.var_blocks = [] def enter_block(self) -> None: self.block_id += 1 @@ -234,8 +236,10 @@ def current_block(self) -> int: def enter_scope(self) -> None: self.var_blocks.append({}) + self.refs.append({}) def leave_scope(self) -> None: + self.flush_refs() self.var_blocks.pop() def is_nested(self) -> int: @@ -244,48 +248,74 @@ def is_nested(self) -> int: def reject_redefinition_of_vars_in_scope(self) -> None: """Make it impossible to redefine defined variables in the current scope. - This is used if we encounter a function definition or break/continue that - can make it ambiguous which definition is live. + This is used if we encounter a function definition that + can make it ambiguous which definition is live. Example: + + x = 0 + + def f() -> int: + return x + + x = '' # Error -- cannot redefine x across function definition """ var_blocks = self.var_blocks[-1] for key in var_blocks: var_blocks[key] = -1 def reject_redefinition_of_vars_in_loop(self) -> None: + """Reject redefinition of variables in the innermost loop. + + If there is an early exit from a loop, there may be ambiguity about which + value may escpae the loop. Example where this matters: + + while f(): + x = 0 + if g(): + break + x = '' # Error -- not a redefinition + reveal_type(x) # int + + This method ensures that the second assignment to 'x' doesn't introduce a new + variable. + """ var_blocks = self.var_blocks[-1] for key, block in var_blocks.items(): if self.block_loop_depth.get(block) == self.loop_depth: var_blocks[key] = -1 - def process_assignment(self, name: str, can_be_redefined: bool, no_value: bool = False) -> bool: - """Record assignment to given name and return True if it defines a new name. + def record_assignment(self, name: str, can_be_redefined: bool, no_value: bool = False) -> bool: + """Record assignment to given name and return True if it defines a new variable. Args: - can_be_redefined: If True, allows assignment in the same block to redefine the name - no_value: If True, the first assignment we encounter will not be considered to redefine - this but to initilize it (in any block) + can_be_redefined: If True, allows assignment in the same block to redefine + this name (if this is a new definition) + no_value: If True, the first assignment we encounter will not be considered + to redefine this but to initilize it (in any block); used for bare "x: t" """ if self.disallow_redef_depth > 0: + # Can't redefine within try/with a block. can_be_redefined = False block = self.current_block() var_blocks = self.var_blocks[-1] uninitialized = self.uninitialized - existing_no_value = name in self.uninitialized + declared_but_no_value = name in self.uninitialized if no_value: uninitialized.add(name) else: uninitialized.discard(name) if name not in var_blocks: - # New definition + # New definition in this scope. if can_be_redefined: + # Store the block where this was defined to allow redefinition in + # the same block only. var_blocks[name] = block else: # This doesn't support arbitrary redefinition. - # TODO: Make this less restricted. var_blocks[name] = -1 return True - elif var_blocks[name] == block and not existing_no_value: - # Redefinition + elif var_blocks[name] == block and not declared_but_no_value: + # Redefinition -- defines a new variable with the same name. return True else: + # Assigns to an existing variable. return False From f153809146fba91f879a83ff79237eeccdba1f0c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 28 Sep 2018 15:53:19 +0100 Subject: [PATCH 14/47] Fix issues --- mypy/semanal_redef.py | 7 +++++-- test-data/unit/check-redefine.test | 10 ++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/mypy/semanal_redef.py b/mypy/semanal_redef.py index b6e3cc7bb3c9..766a377e84c1 100644 --- a/mypy/semanal_redef.py +++ b/mypy/semanal_redef.py @@ -1,4 +1,4 @@ -from typing import Dict, List +from typing import Dict, List, Set from mypy.nodes import ( Block, AssignmentStmt, NameExpr, MypyFile, FuncDef, Lvalue, ListExpr, TupleExpr, TempNode, @@ -103,10 +103,13 @@ def visit_while_stmt(self, stmt: WhileStmt) -> None: self.leave_loop() def visit_for_stmt(self, stmt: ForStmt) -> None: + stmt.expr.accept(self) self.analyze_lvalue(stmt.index, True) self.enter_loop() - super().visit_for_stmt(stmt) + stmt.body.accept(self) self.leave_loop() + if stmt.else_body: + stmt.else_body.accept(self) def visit_break_stmt(self, stmt: BreakStmt) -> None: self.reject_redefinition_of_vars_in_loop() diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index 5103673e8984..3b8c0dbae456 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -338,3 +338,13 @@ x = 1 reveal_type(x) # E: Revealed type is 'builtins.int' if int(): x = object() + +[case testRedefineGlobalUsingForLoop] +from typing import Iterable, TypeVar, Union +T = TypeVar('T') +def f(x: T) -> Iterable[Union[T, str]]: pass +x = 0 +reveal_type(x) # E: Revealed type is 'builtins.int' +for x in f(x): + pass +reveal_type(x) # E: Revealed type is 'Union[builtins.int*, builtins.str]' From 856d401d68e54be1f2603cfb0d6b66a48849361d Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 28 Sep 2018 16:16:22 +0100 Subject: [PATCH 15/47] Fix issues --- mypy/semanal.py | 2 +- mypy/semanal_redef.py | 6 +++++- test-data/unit/check-inference.test | 5 +++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 9fd5ef7a9cd7..43e441ac1348 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2059,7 +2059,7 @@ def analyze_name_lvalue(self, # Define new local name. v = self.make_name_lvalue_var(lval, LDEF) self.add_local(v, lval) - if lval.name == '_': + if unmangle(lval.name) == '_': # Special case for assignment to local named '_': always infer 'Any'. typ = AnyType(TypeOfAny.special_form) self.store_declared_types(lval, typ) diff --git a/mypy/semanal_redef.py b/mypy/semanal_redef.py index 766a377e84c1..7aceb8f58ce8 100644 --- a/mypy/semanal_redef.py +++ b/mypy/semanal_redef.py @@ -3,7 +3,7 @@ from mypy.nodes import ( Block, AssignmentStmt, NameExpr, MypyFile, FuncDef, Lvalue, ListExpr, TupleExpr, TempNode, WhileStmt, ForStmt, BreakStmt, ContinueStmt, TryStmt, WithStmt, StarExpr, ImportFrom, - MemberExpr, IndexExpr + MemberExpr, IndexExpr, Import ) from mypy.traverser import TraverserVisitor @@ -152,6 +152,10 @@ def analyze_lvalue(self, lvalue: Lvalue, has_initializer: bool) -> None: elif isinstance(lvalue, StarExpr): self.analyze_lvalue(lvalue.expr, has_initializer) + def visit_import(self, imp: Import) -> None: + for id, as_id in imp.ids: + self.record_assignment(as_id or id, False, False) + def visit_import_from(self, imp: ImportFrom) -> None: for id, as_id in imp.names: self.record_assignment(as_id or id, False, False) diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 1158d02f6f02..3da0a9bd02c9 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -2429,12 +2429,13 @@ def foo() -> None: [case testUnusedTargetNotGlobal] _ = 0 -_ = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +_ = '' [case testUnusedTargetNotClass] class C: _ = 0 - _ = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") + _ = '' +reveal_type(C._) # E: Revealed type is 'builtins.str' [case testUnusedTargetTupleUnpacking] def foo() -> None: From 865f1dd29e437b6638977e39dd80f3969041d245 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 28 Sep 2018 16:28:52 +0100 Subject: [PATCH 16/47] Work around type check error --- mypy/fscache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/fscache.py b/mypy/fscache.py index 3f1f9e577527..496d1333326b 100644 --- a/mypy/fscache.py +++ b/mypy/fscache.py @@ -144,7 +144,7 @@ def _fake_init(self, path: str) -> os.stat_result: seq[stat.ST_NLINK] = 1 seq[stat.ST_SIZE] = 0 tpl = tuple(seq) - st = os.stat_result(tpl) + st = os.stat_result(tpl) # type: ignore self.stat_cache[path] = st # Make listdir() and read() also pretend this file exists. self.fake_package_cache.add(dirname) From 27a1e57ff8d592d584aaa5898f34b03f60b48b1b Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 4 Oct 2018 15:22:31 +0100 Subject: [PATCH 17/47] Fix merge issue --- test-data/unit/check-expressions.test | 1 - 1 file changed, 1 deletion(-) diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 13eb3ecd7309..c6a53210162a 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -959,7 +959,6 @@ main:3: error: "A" not callable [case testNoneReturnTypeBasics] a, o = None, None # type: (A, object) -<<<<<<< HEAD if int(): a = f() # E: "f" does not return a value if int(): From f04e37a70badb01a194551d62833a3c10474af1f Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 4 Oct 2018 16:13:04 +0100 Subject: [PATCH 18/47] Fix lint --- mypy/semanal.py | 1 - mypy/semanal_redef.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 3da8bd2e1021..2ddd9c4f1521 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2088,7 +2088,6 @@ def is_alias_for_final_name(self, name: str) -> bool: return False existing = self.globals.get(orig_name) return existing is not None and is_final_node(existing.node) - #return is_final_node(self.locals[-1][name].node) def make_name_lvalue_var(self, lvalue: NameExpr, kind: int, inferred: bool) -> Var: """Return a Var node for an lvalue that is a name expression.""" diff --git a/mypy/semanal_redef.py b/mypy/semanal_redef.py index 7aceb8f58ce8..81c1c039b3bb 100644 --- a/mypy/semanal_redef.py +++ b/mypy/semanal_redef.py @@ -137,7 +137,7 @@ def analyze_lvalue(self, lvalue: Lvalue, has_initializer: bool) -> None: if isinstance(lvalue, NameExpr): name = lvalue.name is_new = self.record_assignment(name, True, not has_initializer) - if is_new: # and name != '_': # Underscore gets special handling later + if is_new: # and name != '_': # Underscore gets special handling later self.handle_def(lvalue) else: self.handle_ref(lvalue) From 1f9f62effa4a88751067fe5dab6666eca86bcd1d Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 4 Oct 2018 16:13:17 +0100 Subject: [PATCH 19/47] Fix more tests --- test-data/unit/python2eval.test | 25 ++++++++++++++----------- test-data/unit/pythoneval.test | 25 +++++++++++++++---------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/test-data/unit/python2eval.test b/test-data/unit/python2eval.test index e7f374841cb3..a9a3cffaf3c8 100644 --- a/test-data/unit/python2eval.test +++ b/test-data/unit/python2eval.test @@ -245,22 +245,24 @@ import typing s = '' u = u'' n = 0 -n = s + '' # E -s = s + u'' # E +if int(): + n = s + '' # E + s = s + u'' # E [out] -_program.py:5: error: Incompatible types in assignment (expression has type "str", variable has type "int") -_program.py:6: error: Incompatible types in assignment (expression has type "unicode", variable has type "str") +_program.py:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") +_program.py:7: error: Incompatible types in assignment (expression has type "unicode", variable has type "str") [case testStrJoin_python2] -import typing s = '' u = u'' n = 0 -n = ''.join(['']) # Error -s = ''.join([u'']) # Error +if int(): + n = ''.join(['']) # Error +if int(): + s = ''.join([u'']) # Error [out] _program.py:5: error: Incompatible types in assignment (expression has type "str", variable has type "int") -_program.py:6: error: Incompatible types in assignment (expression has type "unicode", variable has type "str") +_program.py:7: error: Incompatible types in assignment (expression has type "unicode", variable has type "str") [case testNamedTuple_python2] from typing import NamedTuple @@ -280,11 +282,12 @@ _program.py:5: error: "X" has no attribute "c" import typing x = 4j y = x.real -y = x # Error +if int(): + y = x # Error x.imag = 2.0 # Error [out] -_program.py:4: error: Incompatible types in assignment (expression has type "complex", variable has type "float") -_program.py:5: error: Property "imag" defined in "complex" is read-only +_program.py:5: error: Incompatible types in assignment (expression has type "complex", variable has type "float") +_program.py:6: error: Property "imag" defined in "complex" is read-only [case testComplexArithmetic_python2] import typing diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index bcd30c20f158..62f591091a02 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -353,14 +353,18 @@ abs(2.2) + 'x' _program.py:6: error: Unsupported operand types for + ("float" and "str") [case testROperatorMethods] - b = None # type: bytes s = None # type: str -s = b'foo' * 5 # Error -b = 5 * b'foo' -b = b'foo' * 5 -s = 5 * 'foo' -s = 'foo' * 5 +if int(): + s = b'foo' * 5 # Error +if int(): + b = 5 * b'foo' +if int(): + b = b'foo' * 5 +if int(): + s = 5 * 'foo' +if int(): + s = 'foo' * 5 [out] _program.py:4: error: Incompatible types in assignment (expression has type "bytes", variable has type "str") @@ -674,14 +678,15 @@ print(4J / 2.0) 2j [case testComplexArithmetic2] -import typing x = 5 + 8j -x = '' +if int(): + x = '' # E y = 3j * 2.0 -y = '' +if int(): + y = '' # E [out] _program.py:3: error: Incompatible types in assignment (expression has type "str", variable has type "complex") -_program.py:5: error: Incompatible types in assignment (expression has type "str", variable has type "complex") +_program.py:6: error: Incompatible types in assignment (expression has type "str", variable has type "complex") [case testSuperNew] from typing import Dict, Any From 9ba82a9ed6b76289814c80fff33cc9d13d3f3f71 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 4 Oct 2018 16:14:37 +0100 Subject: [PATCH 20/47] Rename semanal_redef -> renaming --- mypy/{semanal_redef.py => renaming.py} | 0 mypy/semanal_pass1.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename mypy/{semanal_redef.py => renaming.py} (100%) diff --git a/mypy/semanal_redef.py b/mypy/renaming.py similarity index 100% rename from mypy/semanal_redef.py rename to mypy/renaming.py diff --git a/mypy/semanal_pass1.py b/mypy/semanal_pass1.py index 7958607352bc..0d66fdd37770 100644 --- a/mypy/semanal_pass1.py +++ b/mypy/semanal_pass1.py @@ -33,7 +33,7 @@ from mypy.options import Options from mypy.sametypes import is_same_type from mypy.visitor import NodeVisitor -from mypy.semanal_redef import VariableRenameVisitor +from mypy.renaming import VariableRenameVisitor class SemanticAnalyzerPass1(NodeVisitor[None]): From fbf052e201f3255fcb8b7abb7daab038f5ffb902 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 9 Oct 2018 11:54:49 +0100 Subject: [PATCH 21/47] Only perform redefinition if variable has been read --- mypy/renaming.py | 75 ++++++++----- test-data/unit/check-abstract.test | 3 +- test-data/unit/check-class-namedtuple.test | 21 ++-- test-data/unit/check-dynamic-typing.test | 9 +- test-data/unit/check-expressions.test | 6 +- test-data/unit/check-final.test | 9 +- test-data/unit/check-inference.test | 9 +- test-data/unit/check-isinstance.test | 24 +++-- test-data/unit/check-overloading.test | 118 ++++++++++++--------- test-data/unit/check-protocols.test | 30 ++++-- test-data/unit/check-redefine.test | 49 +++++++-- test-data/unit/deps.test | 4 +- 12 files changed, 229 insertions(+), 128 deletions(-) diff --git a/mypy/renaming.py b/mypy/renaming.py index 81c1c039b3bb..f04048e02c4f 100644 --- a/mypy/renaming.py +++ b/mypy/renaming.py @@ -50,14 +50,13 @@ def __init__(self) -> None: self.blocks = [] # type: List[int] # List of scopes; each scope maps short name to block id. self.var_blocks = [] # type: List[Dict[str, int]] - # Variables which have no assigned value yet (e.g., "x: t" but no assigment). - # Assignment in any block is considered an initialization. - self.uninitialized = set() # type: Set[str] # References to variables the we may need to rename. List of # scopes; each scope is a mapping from name to list of collections # of names that refer to the same logical variable. self.refs = [] # type: List[Dict[str, List[List[NameExpr]]]] + # Number of reads of the most recent definition of a variable (per scope) + self.num_reads = [] # type: List[Dict[str, int]] def visit_mypy_file(self, file_node: MypyFile) -> None: """Rename variables within a file. @@ -67,8 +66,10 @@ def visit_mypy_file(self, file_node: MypyFile) -> None: self.clear() self.enter_scope() self.enter_block() + for d in file_node.defs: d.accept(self) + self.leave_block() self.leave_scope() @@ -86,6 +87,7 @@ def visit_func_def(self, fdef: FuncDef) -> None: name = arg.variable.name() self.record_assignment(arg.variable.name(), can_be_redefined=True) self.handle_arg(name) + for stmt in fdef.body.body: stmt.accept(self) @@ -105,6 +107,8 @@ def visit_while_stmt(self, stmt: WhileStmt) -> None: def visit_for_stmt(self, stmt: ForStmt) -> None: stmt.expr.accept(self) self.analyze_lvalue(stmt.index, True) + # Also analyze as non-lvalue so that every for loop index variable is assumed to be read. + stmt.index.accept(self) self.enter_loop() stmt.body.accept(self) self.leave_loop() @@ -127,38 +131,41 @@ def visit_with_stmt(self, stmt: WithStmt) -> None: super().visit_with_stmt(stmt) self.leave_with_or_try() + def visit_import(self, imp: Import) -> None: + for id, as_id in imp.ids: + self.record_assignment(as_id or id, False) + + def visit_import_from(self, imp: ImportFrom) -> None: + for id, as_id in imp.names: + self.record_assignment(as_id or id, False) + def visit_assignment_stmt(self, s: AssignmentStmt) -> None: - has_initializer = not isinstance(s.rvalue, TempNode) s.rvalue.accept(self) for lvalue in s.lvalues: - self.analyze_lvalue(lvalue, has_initializer) + self.analyze_lvalue(lvalue) - def analyze_lvalue(self, lvalue: Lvalue, has_initializer: bool) -> None: + def analyze_lvalue(self, lvalue: Lvalue, is_nested: bool = False) -> None: if isinstance(lvalue, NameExpr): name = lvalue.name - is_new = self.record_assignment(name, True, not has_initializer) - if is_new: # and name != '_': # Underscore gets special handling later + is_new = self.record_assignment(name, True) + if is_new: self.handle_def(lvalue) else: + self.handle_refine(lvalue) + if is_nested: + # This allows these to redefined freely. Multiple assignments often define + # dummy variables that are never read. self.handle_ref(lvalue) elif isinstance(lvalue, (ListExpr, TupleExpr)): for item in lvalue.items: - self.analyze_lvalue(item, has_initializer) + self.analyze_lvalue(item, is_nested=True) elif isinstance(lvalue, MemberExpr): lvalue.expr.accept(self) elif isinstance(lvalue, IndexExpr): lvalue.base.accept(self) lvalue.index.accept(self) elif isinstance(lvalue, StarExpr): - self.analyze_lvalue(lvalue.expr, has_initializer) - - def visit_import(self, imp: Import) -> None: - for id, as_id in imp.ids: - self.record_assignment(as_id or id, False, False) - - def visit_import_from(self, imp: ImportFrom) -> None: - for id, as_id in imp.names: - self.record_assignment(as_id or id, False, False) + self.analyze_lvalue(lvalue.expr) def visit_name_expr(self, expr: NameExpr) -> None: self.handle_ref(expr) @@ -172,8 +179,19 @@ def handle_arg(self, name: str) -> None: def handle_def(self, expr: NameExpr) -> None: """Store new name definition.""" - names = self.refs[-1].setdefault(expr.name, []) + name = expr.name + names = self.refs[-1].setdefault(name, []) names.append([expr]) + self.num_reads[-1][name] = 0 + + def handle_refine(self, expr: NameExpr) -> None: + """Store assignment to an existing name (that replaces previous value, if any).""" + name = expr.name + if name in self.refs[-1]: + names = self.refs[-1][name] + if not names: + names.append([]) + names[-1].append(expr) def handle_ref(self, expr: NameExpr) -> None: """Store reference to defined name.""" @@ -183,6 +201,8 @@ def handle_ref(self, expr: NameExpr) -> None: if not names: names.append([]) names[-1].append(expr) + num_reads = self.num_reads[-1] + num_reads[name] = num_reads.get(name, 0) + 1 def flush_refs(self) -> None: """Rename all references within the current scope. @@ -244,10 +264,12 @@ def current_block(self) -> int: def enter_scope(self) -> None: self.var_blocks.append({}) self.refs.append({}) + self.num_reads.append({}) def leave_scope(self) -> None: self.flush_refs() self.var_blocks.pop() + self.num_reads.pop() def is_nested(self) -> int: return len(self.var_blocks) > 1 @@ -290,26 +312,21 @@ def reject_redefinition_of_vars_in_loop(self) -> None: if self.block_loop_depth.get(block) == self.loop_depth: var_blocks[key] = -1 - def record_assignment(self, name: str, can_be_redefined: bool, no_value: bool = False) -> bool: + def record_assignment(self, name: str, can_be_redefined: bool) -> bool: """Record assignment to given name and return True if it defines a new variable. Args: can_be_redefined: If True, allows assignment in the same block to redefine this name (if this is a new definition) - no_value: If True, the first assignment we encounter will not be considered - to redefine this but to initilize it (in any block); used for bare "x: t" """ + if self.num_reads[-1].get(name, -1) == 0: + # Only set, not read, so no reason to redefine + return False if self.disallow_redef_depth > 0: # Can't redefine within try/with a block. can_be_redefined = False block = self.current_block() var_blocks = self.var_blocks[-1] - uninitialized = self.uninitialized - declared_but_no_value = name in self.uninitialized - if no_value: - uninitialized.add(name) - else: - uninitialized.discard(name) if name not in var_blocks: # New definition in this scope. if can_be_redefined: @@ -320,7 +337,7 @@ def record_assignment(self, name: str, can_be_redefined: bool, no_value: bool = # This doesn't support arbitrary redefinition. var_blocks[name] = -1 return True - elif var_blocks[name] == block and not declared_but_no_value: + elif var_blocks[name] == block: # Redefinition -- defines a new variable with the same name. return True else: diff --git a/test-data/unit/check-abstract.test b/test-data/unit/check-abstract.test index b662fc1be473..583275dcdf16 100644 --- a/test-data/unit/check-abstract.test +++ b/test-data/unit/check-abstract.test @@ -230,7 +230,8 @@ class C(B): var: Type[A] var() -var = A # E: Can only assign concrete classes to a variable of type "Type[A]" +if int(): + var = A # E: Can only assign concrete classes to a variable of type "Type[A]" if int(): var = B # E: Can only assign concrete classes to a variable of type "Type[A]" if int(): diff --git a/test-data/unit/check-class-namedtuple.test b/test-data/unit/check-class-namedtuple.test index b7509f552359..e127cc1b6f0a 100644 --- a/test-data/unit/check-class-namedtuple.test +++ b/test-data/unit/check-class-namedtuple.test @@ -192,13 +192,20 @@ class B(A): pass a = A(1, '') b = B(1, '') t: Tuple[int, str] -b = a # E: Incompatible types in assignment (expression has type "A", variable has type "B") -a = t # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "A") -b = t # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "B") -t = a -t = (1, '') -t = b -a = b +if int(): + b = a # E: Incompatible types in assignment (expression has type "A", variable has type "B") +if int(): + a = t # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "A") +if int(): + b = t # E: Incompatible types in assignment (expression has type "Tuple[int, str]", variable has type "B") +if int(): + t = a +if int(): + t = (1, '') +if int(): + t = b +if int(): + a = b [case testNewNamedTupleSimpleTypeInference] # flags: --python-version 3.6 diff --git a/test-data/unit/check-dynamic-typing.test b/test-data/unit/check-dynamic-typing.test index 8aebefbb947c..1680a0ae5349 100644 --- a/test-data/unit/check-dynamic-typing.test +++ b/test-data/unit/check-dynamic-typing.test @@ -7,9 +7,12 @@ from typing import Any d = None # type: Any a = None # type: A -a = d # Everything ok -d = a -d = d +if int(): + a = d # Everything ok +if int(): + d = a +if int(): + d = d d.x = a d.x = d diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index c6a53210162a..e7359b7ae015 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -1352,8 +1352,10 @@ def f(x: int): pass from typing import List a: List[A] b: List[B] -b = [f(x) for x in a] -a = [f(x) for x in a] # E: List comprehension has incompatible type List[B]; expected List[A] +if int(): + b = [f(x) for x in a] +if int(): + a = [f(x) for x in a] # E: List comprehension has incompatible type List[B]; expected List[A] ([f(x) for x in b]) # E: Argument 1 to "f" has incompatible type "B"; expected "A" class A: pass class B: pass diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index d4f4ba5333b5..85fce089c90f 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -339,6 +339,7 @@ class C: from typing import Final x: Final = 1 +x x = 2 # E: Cannot assign to final name "x" def f() -> int: global x @@ -346,11 +347,13 @@ def f() -> int: return x x2: Final = 1 +x2 def f2() -> None: global x2 x2 = 1 # E: Cannot assign to final name "x2" -y = 1 +y = 1 # E: Cannot asign to final name "y" +y y: Final = 2 # E: Cannot redefine an existing name as final y = 3 # No error here, first definition wins @@ -415,9 +418,9 @@ from typing import Final class C: x: Final = 1 - x = 2 # E: Cannot assign to final attribute "x" + x = 2 # E: Cannot assign to final name "x" - y = 1 + y = 1 # E: Cannot assign to final name "y" y: Final = 2 # E: Cannot redefine an existing name as final [out] diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index b76430a0ab5f..57de8b529547 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -1048,6 +1048,7 @@ class B: pass def f() -> None: for a in [A()]: pass a = A() + a if int(): a = B() \ # E: Incompatible types in assignment (expression has type "B", variable has type "A") @@ -2428,7 +2429,7 @@ def foo() -> None: [case testUnusedTargetNotGlobal] _ = 0 -_ = '' +_ = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") [case testUnusedTargetNotClass] class C: @@ -2470,7 +2471,7 @@ def f(): pass [file m.py] def f(): pass _ = f -_ = 0 +_ = 0 # E: Incompatible types in assignment (expression has type "int", variable has type "Callable[[], Any]") [file a.py] def foo() -> None: import _ @@ -2484,8 +2485,8 @@ def foo() -> None: [file c.py] def foo() -> None: from m import _ - _() # E: "int" not callable - _ = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") + _() + _ = '' # E: Incompatible types in assignment (expression has type "str", variable has type "Callable[[], Any]") [file d.py] def foo() -> None: from m import f as _ diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index 314dfbed4b22..e54b2c1c24e6 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -123,12 +123,13 @@ x = 'a' [case testUnionMultiAssignment] from typing import Union x = None # type: Union[int, str] -x = 1 -x = 'a' -x + 1 # E: Unsupported operand types for + ("str" and "int") -x = 1 -(x, y) = ('a', 1) -x + 1 # E: Unsupported operand types for + ("str" and "int") +if int(): + x = 1 + x = 'a' + x + 1 # E: Unsupported operand types for + ("str" and "int") + x = 1 + (x, y) = ('a', 1) + x + 1 # E: Unsupported operand types for + ("str" and "int") [builtins fixtures/isinstancelist.pyi] [case testUnionIfZigzag] @@ -457,11 +458,12 @@ from typing import Union def foo() -> Union[int, str]: pass x = foo() -x = 1 -x = x + 1 -x = foo() -x = x + 1 # E: Unsupported operand types for + ("str" and "int") \ - # N: Left operand is of type "Union[int, str]" +if int(): + x = 1 + x = x + 1 + x = foo() + x = x + 1 # E: Unsupported operand types for + ("str" and "int") \ + # N: Left operand is of type "Union[int, str]" if isinstance(x, str): x = x + 1 # E: Unsupported operand types for + ("str" and "int") x = 1 diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index cf41a9c5526c..1a264b409012 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -3578,17 +3578,20 @@ def narrow_int(x: Union[int, str]) -> Union[int, NoReturn]: def test_narrow_int() -> None: a: Union[int, str] - a = narrow_int(a) - reveal_type(a) # E: Revealed type is 'builtins.int' + if int(): + a = narrow_int(a) + reveal_type(a) # E: Revealed type is 'builtins.int' b: int - b = narrow_int(b) - reveal_type(b) # E: Revealed type is 'builtins.int' + if int(): + b = narrow_int(b) + reveal_type(b) # E: Revealed type is 'builtins.int' c: str - c = narrow_int(c) - reveal_type(c) # Note: branch is now dead, so no type is revealed - # TODO: maybe we should make mypy report a warning instead? + if int(): + c = narrow_int(c) + reveal_type(c) # Note: branch is now dead, so no type is revealed + # TODO: maybe we should make mypy report a warning instead? [builtins fixtures/isinstance.pyi] [typing fixtures/typing-full.pyi] @@ -3607,17 +3610,20 @@ def narrow_int(x: Union[int, str]) -> Union[int, NoReturn]: def test_narrow_int() -> None: a: Union[int, str] - a = narrow_int(a) - reveal_type(a) # E: Revealed type is 'builtins.int' + if int(): + a = narrow_int(a) + reveal_type(a) # E: Revealed type is 'builtins.int' b: int - b = narrow_int(b) - reveal_type(b) # E: Revealed type is 'builtins.int' + if int(): + b = narrow_int(b) + reveal_type(b) # E: Revealed type is 'builtins.int' c: str - c = narrow_int(c) - reveal_type(c) # Note: branch is now dead, so no type is revealed - # TODO: maybe we should make mypy report a warning instead? + if int(): + c = narrow_int(c) + reveal_type(c) # Note: branch is now dead, so no type is revealed + # TODO: maybe we should make mypy report a warning instead? [builtins fixtures/isinstance.pyi] [typing fixtures/typing-full.pyi] @@ -3637,16 +3643,19 @@ def narrow_none(x: Optional[T]) -> Union[NoReturn, T]: def test_narrow_none() -> None: a: Optional[int] - a = narrow_none(a) - reveal_type(a) # E: Revealed type is 'Union[builtins.int, None]' + if int(): + a = narrow_none(a) + reveal_type(a) # E: Revealed type is 'Union[builtins.int, None]' b: int - b = narrow_none(b) - reveal_type(b) # E: Revealed type is 'builtins.int' + if int(): + b = narrow_none(b) + reveal_type(b) # E: Revealed type is 'builtins.int' c: None - c = narrow_none(c) - reveal_type(c) # Note: branch is now dead, so no type is revealed + if int(): + c = narrow_none(c) + reveal_type(c) # Note: branch is now dead, so no type is revealed [builtins fixtures/isinstance.pyi] [typing fixtures/typing-full.pyi] @@ -3666,16 +3675,19 @@ def narrow_none(x: Optional[T]) -> Union[NoReturn, T]: def test_narrow_none() -> None: a: Optional[int] - a = narrow_none(a) - reveal_type(a) # E: Revealed type is 'builtins.int' + if int(): + a = narrow_none(a) + reveal_type(a) # E: Revealed type is 'builtins.int' b: int - b = narrow_none(b) - reveal_type(b) # E: Revealed type is 'builtins.int' + if int(): + b = narrow_none(b) + reveal_type(b) # E: Revealed type is 'builtins.int' c: None - c = narrow_none(c) - reveal_type(c) # Branch is now dead + if int(): + c = narrow_none(c) + reveal_type(c) # Branch is now dead [builtins fixtures/isinstance.pyi] [typing fixtures/typing-full.pyi] @@ -3695,16 +3707,19 @@ def narrow_none_v2(x: Optional[T]) -> T: def test_narrow_none_v2() -> None: a: Optional[int] - a = narrow_none_v2(a) - reveal_type(a) # E: Revealed type is 'Union[builtins.int, None]' + if int(): + a = narrow_none_v2(a) + reveal_type(a) # E: Revealed type is 'Union[builtins.int, None]' b: int - b = narrow_none_v2(b) - reveal_type(b) # E: Revealed type is 'builtins.int' + if int(): + b = narrow_none_v2(b) + reveal_type(b) # E: Revealed type is 'builtins.int' c: None - c = narrow_none_v2(c) - reveal_type(c) # Note: branch is now dead, so no type is revealed + if int(): + c = narrow_none_v2(c) + reveal_type(c) # Note: branch is now dead, so no type is revealed [builtins fixtures/isinstance.pyi] [typing fixtures/typing-full.pyi] @@ -3723,16 +3738,19 @@ def narrow_none_v2(x: Optional[T]) -> T: def test_narrow_none_v2() -> None: a: Optional[int] - a = narrow_none_v2(a) - reveal_type(a) # E: Revealed type is 'builtins.int' + if int(): + a = narrow_none_v2(a) + reveal_type(a) # E: Revealed type is 'builtins.int' b: int - b = narrow_none_v2(b) - reveal_type(b) # E: Revealed type is 'builtins.int' + if int(): + b = narrow_none_v2(b) + reveal_type(b) # E: Revealed type is 'builtins.int' c: None - c = narrow_none_v2(c) - reveal_type(c) # Note: branch is now dead, so no type is revealed + if int(): + c = narrow_none_v2(c) + reveal_type(c) # Note: branch is now dead, so no type is revealed [builtins fixtures/isinstance.pyi] [typing fixtures/typing-full.pyi] @@ -3755,12 +3773,14 @@ def narrow_to_not_a(x: T) -> Union[NoReturn, T]: def test() -> None: val: Union[A, B] - val = narrow_to_not_a(val) - reveal_type(val) # E: Revealed type is '__main__.B' + if int(): + val = narrow_to_not_a(val) + reveal_type(val) # E: Revealed type is '__main__.B' val2: A - val2 = narrow_to_not_a(val2) - reveal_type(val2) # Branch now dead + if int(): + val2 = narrow_to_not_a(val2) + reveal_type(val2) # Branch now dead [builtins fixtures/isinstance.pyi] [typing fixtures/typing-full.pyi] @@ -3781,14 +3801,14 @@ def narrow_to_not_a_v2(x: T) -> T: assert not isinstance(x, A) return x -def test_v2() -> None: - val: Union[A, B] - val = narrow_to_not_a_v2(val) - reveal_type(val) # E: Revealed type is '__main__.B' +def test_v2(val: Union[A, B], val2: A) -> None: + if int(): + val = narrow_to_not_a_v2(val) + reveal_type(val) # E: Revealed type is '__main__.B' - val2: A - val2 = narrow_to_not_a_v2(val2) - reveal_type(val2) # Branch now dead + if int(): + val2 = narrow_to_not_a_v2(val2) + reveal_type(val2) # Branch now dead [builtins fixtures/isinstance.pyi] [typing fixtures/typing-full.pyi] diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 2738b9972c36..cf2af23cef11 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -149,15 +149,20 @@ c1: C1 c2: C2 y: AnotherP -x = c +if int(): + x = c if int(): x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "P") +if int(): x = c1 # E: Incompatible types in assignment (expression has type "C1", variable has type "P") \ # N: 'C1' is missing following 'P' protocol member: \ # N: meth2 +if int(): x = c2 +if int(): x = y -y = x +if int(): + y = x [case testSimpleProtocolTwoMethodsExtend] from typing import Protocol @@ -183,7 +188,8 @@ x: P2 reveal_type(x.meth1()) # E: Revealed type is 'builtins.int' reveal_type(x.meth2()) # E: Revealed type is 'builtins.str' -x = C() # OK +if int(): + x = C() # OK if int(): x = Cbad() # E: Incompatible types in assignment (expression has type "Cbad", variable has type "P2") \ # N: 'Cbad' is missing following 'P2' protocol member: \ @@ -444,18 +450,24 @@ class B(A): pass x1: Pco[B] y1: Pco[A] -x1 = y1 # E: Incompatible types in assignment (expression has type "Pco[A]", variable has type "Pco[B]") -y1 = x1 # E: Incompatible types in assignment (expression has type "Pco[B]", variable has type "Pco[A]") +if int(): + x1 = y1 # E: Incompatible types in assignment (expression has type "Pco[A]", variable has type "Pco[B]") +if int(): + y1 = x1 # E: Incompatible types in assignment (expression has type "Pco[B]", variable has type "Pco[A]") x2: Pcontra[B] y2: Pcontra[A] -y2 = x2 # E: Incompatible types in assignment (expression has type "Pcontra[B]", variable has type "Pcontra[A]") -x2 = y2 # E: Incompatible types in assignment (expression has type "Pcontra[A]", variable has type "Pcontra[B]") +if int(): + y2 = x2 # E: Incompatible types in assignment (expression has type "Pcontra[B]", variable has type "Pcontra[A]") +if int(): + x2 = y2 # E: Incompatible types in assignment (expression has type "Pcontra[A]", variable has type "Pcontra[B]") x3: Pinv[B] y3: Pinv[A] -y3 = x3 # E: Incompatible types in assignment (expression has type "Pinv[B]", variable has type "Pinv[A]") -x3 = y3 # E: Incompatible types in assignment (expression has type "Pinv[A]", variable has type "Pinv[B]") +if int(): + y3 = x3 # E: Incompatible types in assignment (expression has type "Pinv[B]", variable has type "Pinv[A]") +if int(): + x3 = y3 # E: Incompatible types in assignment (expression has type "Pinv[A]", variable has type "Pinv[B]") [case testProtocolVarianceWithCallableAndList] from typing import Protocol, TypeVar, Callable, List diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index 3b8c0dbae456..b0d306c1a5be 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -76,12 +76,14 @@ def g(a: Iterable[int]) -> None: def h(a: Iterable[int]) -> None: x = '' + reveal_type(x) # E: Revealed type is 'builtins.str' for x in a: pass [case testCannotRedefineLocalWithinTry] def f() -> None: try: x = 0 + x g() # Might raise an exception x = '' \ # E: Incompatible types in assignment (expression has type "str", variable has type "int") @@ -89,6 +91,7 @@ def f() -> None: pass reveal_type(x) # E: Revealed type is 'builtins.int' y = 0 + y y = '' def g(): pass @@ -97,11 +100,13 @@ def g(): pass def f() -> None: with g(): x = 0 + x g() # Might raise an exception x = '' \ # E: Incompatible types in assignment (expression has type "str", variable has type "int") reveal_type(x) # E: Revealed type is 'builtins.int' y = 0 + y y = '' def g(): pass @@ -109,6 +114,7 @@ def g(): pass [case testCannotRedefineAcrossNestedFunction] def f() -> None: x = 0 + x def g() -> None: x g() @@ -116,6 +122,7 @@ def f() -> None: # E: Incompatible types in assignment (expression has type "str", variable has type "int") g() y = 0 + y y = '' [case testCannotRedefineAcrossNestedDecoratedFunction] @@ -123,6 +130,7 @@ def dec(f): return f def f() -> None: x = 0 + x @dec def g() -> None: x @@ -131,6 +139,7 @@ def f() -> None: # E: Incompatible types in assignment (expression has type "str", variable has type "int") g() y = 0 + y y = '' [case testCannotRedefineAcrossNestedOverloadedFunction] @@ -138,6 +147,7 @@ from typing import overload def f() -> None: x = 0 + x @overload def g() -> None: pass @overload @@ -149,6 +159,7 @@ def f() -> None: # E: Incompatible types in assignment (expression has type "str", variable has type "int") g() y = 0 + y y = '' [case testRedefineLocalInMultipleAssignment] @@ -174,8 +185,10 @@ def f() -> None: [case testRedefineWithBreakAndContinue] def f() -> None: y = 0 + y while int(): z = 0 + z z = '' x = 0 if int(): @@ -187,8 +200,10 @@ def f() -> None: def g() -> None: y = 0 + y for a in h(): z = 0 + z z = '' x = 0 if int(): @@ -203,11 +218,14 @@ def h(): pass [case testRedefineLocalAndNestedLoops] def f() -> None: z = 0 + z while int(): x = 0 + x while int(): if 1: y = 1 + y if int(): break y = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") @@ -233,13 +251,13 @@ def f() -> None: [case testRedefineVarAsTypeVar] from typing import TypeVar def f() -> None: - # Since these look like assignments, they can be redefined. x = TypeVar('x') - x = 1 + x = 1 # E: Invalid assignment target reveal_type(x) # E: Revealed type is 'builtins.int' y = 1 - y = TypeVar('y') - # TODO: This should either work or we should give an error earlier. + # NOTE: '"int" not callable' is due to test stubs + y = TypeVar('y') # E: Cannot redefine 'y' as a type variable \ + # E: "int" not callable def h(a: y) -> y: return a # E: Invalid type "y" [case testCannotRedefineVarAsModule] @@ -253,17 +271,20 @@ def f() -> None: [case testRedefineLocalWithTypeAnnotation] def f() -> None: x = 1 + reveal_type(x) # E: Revealed type is 'builtins.int' x = '' # type: object - reveal_type(x) # E: Revealed type is 'builtins.object' + reveal_type(x) # E: Revealed type is 'builtins.object' def g() -> None: x = 1 + reveal_type(x) # E: Revealed type is 'builtins.int' x: object = '' - reveal_type(x) # E: Revealed type is 'builtins.object' + reveal_type(x) # E: Revealed type is 'builtins.object' def h() -> None: x: int x = 1 + reveal_type(x) # E: Revealed type is 'builtins.int' x: object - x: object = '' # E: Name 'x' already defined on line 12 + x: object = '' # E: Name 'x' already defined on line 15 def farg(x: int) -> None: x: str = '' @@ -333,6 +354,7 @@ x = '' reveal_type(x) # E: Revealed type is 'builtins.str' x: int x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +reveal_type(x) # E: Revealed type is 'builtins.int' x: object x = 1 reveal_type(x) # E: Revealed type is 'builtins.int' @@ -348,3 +370,16 @@ reveal_type(x) # E: Revealed type is 'builtins.int' for x in f(x): pass reveal_type(x) # E: Revealed type is 'Union[builtins.int*, builtins.str]' + +[case testNoRedefinitionIfOnlyInitialized] +x = None # type: int +x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +x = object() # E: Incompatible types in assignment (expression has type "object", variable has type "int") +x # Reference to variable +x = '' + +[case testXXX] +x: int +x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +reveal_type(x) # E: Revealed type is 'builtins.int' +x: object diff --git a/test-data/unit/deps.test b/test-data/unit/deps.test index 02cf1c6db35e..fdb16a87346d 100644 --- a/test-data/unit/deps.test +++ b/test-data/unit/deps.test @@ -1202,10 +1202,8 @@ def func() -> int: pass [out] -> m - -> m -> m - -> , m, m.outer ---' + -> m, m.outer [case testLogicalDefinitionMember] # flags: --logical-deps From 1116b477ee1030af9981e1251fd437db53bb6a78 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 9 Oct 2018 15:06:57 +0100 Subject: [PATCH 22/47] A few fixes --- mypy/renaming.py | 3 +- test-data/unit/check-final.test | 8 +- test-data/unit/check-inference.test | 4 +- test-data/unit/check-redefine.test | 5 +- test-data/unit/semanal-basic.test | 6 +- test-data/unit/semanal-expressions.test | 12 +-- test-data/unit/semanal-statements.test | 104 +++++++++++++----------- 7 files changed, 78 insertions(+), 64 deletions(-) diff --git a/mypy/renaming.py b/mypy/renaming.py index f04048e02c4f..249b879b760f 100644 --- a/mypy/renaming.py +++ b/mypy/renaming.py @@ -165,7 +165,7 @@ def analyze_lvalue(self, lvalue: Lvalue, is_nested: bool = False) -> None: lvalue.base.accept(self) lvalue.index.accept(self) elif isinstance(lvalue, StarExpr): - self.analyze_lvalue(lvalue.expr) + self.analyze_lvalue(lvalue.expr, is_nested=is_nested) def visit_name_expr(self, expr: NameExpr) -> None: self.handle_ref(expr) @@ -176,6 +176,7 @@ def handle_arg(self, name: str) -> None: """Store function argument.""" if name not in self.refs[-1]: self.refs[-1][name] = [[]] + self.num_reads[-1][name] = 0 def handle_def(self, expr: NameExpr) -> None: """Store new name definition.""" diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index 85fce089c90f..17991ea95db9 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -352,15 +352,15 @@ def f2() -> None: global x2 x2 = 1 # E: Cannot assign to final name "x2" -y = 1 # E: Cannot asign to final name "y" +y = 1 y y: Final = 2 # E: Cannot redefine an existing name as final -y = 3 # No error here, first definition wins +y = 3 # E: Cannot assign to final name "y" z: Final = 1 -z: Final = 2 # E: Cannot redefine an existing name as final +z: Final = 2 # E: Name 'z' already defined on line 22 \ + # E: Cannot redefine an existing name as final z = 3 # E: Cannot assign to final name "z" -[out] [case testFinalReassignModuleReexport] from typing import Final diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 57de8b529547..8e9afd7e0408 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -2433,7 +2433,7 @@ _ = '' # E: Incompatible types in assignment (expression has type "str", variabl [case testUnusedTargetNotClass] class C: - _ = 0 + _, _ = 0, 0 _ = '' reveal_type(C._) # E: Revealed type is 'builtins.str' @@ -2494,7 +2494,7 @@ def foo() -> None: _ = 0 # E: Incompatible types in assignment (expression has type "int", variable has type "Callable[[], Any]") [builtins fixtures/module.pyi] -[case testUnusedTargetNotClass] +[case testUnusedTargetNotClass2] def foo() -> None: class _: pass diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index b0d306c1a5be..e2670f9f4bd7 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -24,6 +24,7 @@ def f() -> None: [case testRedefineFunctionArg] def f(x: int) -> None: + g(x) x = '' reveal_type(x) # E: Revealed type is 'builtins.str' def g(x: int) -> None: @@ -286,7 +287,9 @@ def h() -> None: x: object x: object = '' # E: Name 'x' already defined on line 15 def farg(x: int) -> None: - x: str = '' + x: str = '' # E: Name 'x' already defined on line 17 +def farg2(x: int) -> None: + x: str = x # E: Incompatible types in assignment (expression has type "int", variable has type "str") [case testRedefineLocalWithTypeAnnotationSpecialCases] def f() -> None: diff --git a/test-data/unit/semanal-basic.test b/test-data/unit/semanal-basic.test index 238544ff8ea9..d7ef830eb2d7 100644 --- a/test-data/unit/semanal-basic.test +++ b/test-data/unit/semanal-basic.test @@ -345,13 +345,13 @@ MypyFile:1( NameExpr(object [builtins.object])) Then( AssignmentStmt:2( - NameExpr(x* [__main__.x]) + NameExpr(x'* [__main__.x']) CallExpr:2( NameExpr(object [builtins.object]) Args())) AssignmentStmt:3( - NameExpr(x [__main__.x]) - NameExpr(x [__main__.x])))) + NameExpr(x* [__main__.x]) + NameExpr(x' [__main__.x'])))) ExpressionStmt:4( NameExpr(x [__main__.x]))) diff --git a/test-data/unit/semanal-expressions.test b/test-data/unit/semanal-expressions.test index 32a28f7fe879..1ddc9c20e587 100644 --- a/test-data/unit/semanal-expressions.test +++ b/test-data/unit/semanal-expressions.test @@ -219,14 +219,14 @@ MypyFile:1( [case testListComprehensionWithCondition] a = 0 -a = [x for x in a if x] +b = [x for x in a if x] [out] MypyFile:1( AssignmentStmt:1( NameExpr(a* [__main__.a]) IntExpr(0)) AssignmentStmt:2( - NameExpr(a [__main__.a]) + NameExpr(b* [__main__.b]) ListComprehension:2( GeneratorExpr:2( NameExpr(x [l]) @@ -254,14 +254,14 @@ MypyFile:1( [case testSetComprehensionWithCondition] a = 0 -a = {x for x in a if x} +b = {x for x in a if x} [out] MypyFile:1( AssignmentStmt:1( NameExpr(a* [__main__.a]) IntExpr(0)) AssignmentStmt:2( - NameExpr(a [__main__.a]) + NameExpr(b* [__main__.b]) SetComprehension:2( GeneratorExpr:2( NameExpr(x [l]) @@ -289,14 +289,14 @@ MypyFile:1( [case testDictionaryComprehensionWithCondition] a = 0 -a = {x: x + 1 for x in a if x} +b = {x: x + 1 for x in a if x} [out] MypyFile:1( AssignmentStmt:1( NameExpr(a* [__main__.a]) IntExpr(0)) AssignmentStmt:2( - NameExpr(a [__main__.a]) + NameExpr(b* [__main__.b]) DictionaryComprehension:2( NameExpr(x [l]) OpExpr:2( diff --git a/test-data/unit/semanal-statements.test b/test-data/unit/semanal-statements.test index c879dd5e342c..a24c8ea52f55 100644 --- a/test-data/unit/semanal-statements.test +++ b/test-data/unit/semanal-statements.test @@ -164,12 +164,12 @@ for x in None: [out] MypyFile:1( ForStmt:1( - NameExpr(x* [__main__.x]) + NameExpr(x'* [__main__.x']) NameExpr(None [builtins.None]) Block:1( PassStmt:2())) ForStmt:3( - NameExpr(x [__main__.x]) + NameExpr(x* [__main__.x]) NameExpr(None [builtins.None]) Block:3( PassStmt:4()))) @@ -191,7 +191,7 @@ MypyFile:1( Block:2( PassStmt:3())) ForStmt:4( - NameExpr(x [l]) + NameExpr(x'* [l]) NameExpr(None [builtins.None]) Block:4( PassStmt:5()))))) @@ -310,12 +310,12 @@ MypyFile:1( [case testLvalues] x = y = 1 -x = 1 +xx = 1 x.m = 1 x[y] = 1 -x, y = 1 -[x, y] = 1 -(x, y) = 1 +x2, y2 = 1 +[x3, y3] = 1 +(x4, y4) = 1 [out] MypyFile:1( AssignmentStmt:1( @@ -324,7 +324,7 @@ MypyFile:1( NameExpr(y* [__main__.y])) IntExpr(1)) AssignmentStmt:2( - NameExpr(x [__main__.x]) + NameExpr(xx* [__main__.xx]) IntExpr(1)) AssignmentStmt:3( MemberExpr:3( @@ -338,18 +338,18 @@ MypyFile:1( IntExpr(1)) AssignmentStmt:5( TupleExpr:5( - NameExpr(x [__main__.x]) - NameExpr(y [__main__.y])) + NameExpr(x2* [__main__.x2]) + NameExpr(y2* [__main__.y2])) IntExpr(1)) AssignmentStmt:6( TupleExpr:6( - NameExpr(x [__main__.x]) - NameExpr(y [__main__.y])) + NameExpr(x3* [__main__.x3]) + NameExpr(y3* [__main__.y3])) IntExpr(1)) AssignmentStmt:7( TupleExpr:7( - NameExpr(x [__main__.x]) - NameExpr(y [__main__.y])) + NameExpr(x4* [__main__.x4]) + NameExpr(y4* [__main__.y4])) IntExpr(1))) [case testStarLvalues] @@ -361,15 +361,15 @@ MypyFile:1( AssignmentStmt:1( TupleExpr:1( StarExpr:1( - NameExpr(x* [__main__.x])) - NameExpr(y* [__main__.y])) + NameExpr(x'* [__main__.x'])) + NameExpr(y'* [__main__.y'])) IntExpr(1)) AssignmentStmt:2( TupleExpr:2( StarExpr:2( - NameExpr(x [__main__.x])) + NameExpr(x''* [__main__.x''])) TupleExpr:2( - NameExpr(y [__main__.y]) + NameExpr(y* [__main__.y]) StarExpr:2( NameExpr(z* [__main__.z])))) IntExpr(1)) @@ -377,7 +377,7 @@ MypyFile:1( TupleExpr:3( StarExpr:3( TupleExpr:3( - NameExpr(x [__main__.x]) + NameExpr(x* [__main__.x]) NameExpr(q* [__main__.q]))) NameExpr(r* [__main__.r])) IntExpr(1))) @@ -389,13 +389,13 @@ x, y = 2 MypyFile:1( AssignmentStmt:1( TupleExpr:1( - NameExpr(x* [__main__.x]) - NameExpr(y* [__main__.y])) + NameExpr(x'* [__main__.x']) + NameExpr(y'* [__main__.y'])) IntExpr(1)) AssignmentStmt:2( TupleExpr:2( - NameExpr(x [__main__.x]) - NameExpr(y [__main__.y])) + NameExpr(x* [__main__.x]) + NameExpr(y* [__main__.y])) IntExpr(2))) [case testComplexDefinitions] @@ -458,27 +458,32 @@ MypyFile:1( [case testMultipleDefOnlySomeNewNestedLists] x = 1 -y, [x, z] = 1 -[p, [x, r]] = 1 +if x: + y, [x, z] = 1 + [p, [x, r]] = 1 [out] MypyFile:1( AssignmentStmt:1( NameExpr(x* [__main__.x]) IntExpr(1)) - AssignmentStmt:2( - TupleExpr:2( - NameExpr(y* [__main__.y]) - TupleExpr:2( - NameExpr(x [__main__.x]) - NameExpr(z* [__main__.z]))) - IntExpr(1)) - AssignmentStmt:3( - TupleExpr:3( - NameExpr(p* [__main__.p]) - TupleExpr:3( - NameExpr(x [__main__.x]) - NameExpr(r* [__main__.r]))) - IntExpr(1))) + IfStmt:2( + If( + NameExpr(x [__main__.x])) + Then( + AssignmentStmt:3( + TupleExpr:3( + NameExpr(y* [__main__.y]) + TupleExpr:3( + NameExpr(x [__main__.x]) + NameExpr(z* [__main__.z]))) + IntExpr(1)) + AssignmentStmt:4( + TupleExpr:4( + NameExpr(p* [__main__.p]) + TupleExpr:4( + NameExpr(x [__main__.x]) + NameExpr(r* [__main__.r]))) + IntExpr(1))))) [case testIndexedDel] x = y = 1 @@ -712,7 +717,8 @@ MypyFile:1( [case testVariableInBlock] while object: x = None - x = x + if x: + x = x [out] MypyFile:1( WhileStmt:1( @@ -721,9 +727,13 @@ MypyFile:1( AssignmentStmt:2( NameExpr(x* [__main__.x]) NameExpr(None [builtins.None])) - AssignmentStmt:3( - NameExpr(x [__main__.x]) - NameExpr(x [__main__.x]))))) + IfStmt:3( + If( + NameExpr(x [__main__.x])) + Then( + AssignmentStmt:4( + NameExpr(x [__main__.x]) + NameExpr(x [__main__.x]))))))) [case testVariableInExceptHandler] try: @@ -809,15 +819,15 @@ x, o = o, o [out] MypyFile:1( AssignmentStmt:1( - NameExpr(o* [__main__.o]) + NameExpr(o'* [__main__.o']) NameExpr(None [builtins.None])) AssignmentStmt:2( TupleExpr:2( NameExpr(x* [__main__.x]) - NameExpr(o [__main__.o])) + NameExpr(o* [__main__.o])) TupleExpr:2( - NameExpr(o [__main__.o]) - NameExpr(o [__main__.o])))) + NameExpr(o' [__main__.o']) + NameExpr(o' [__main__.o'])))) [case testFunctionDecorator] def decorate(f): pass From 70a3085c44d7c06cba393b7d553d927f21e9666f Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 9 Oct 2018 15:32:01 +0100 Subject: [PATCH 23/47] Add hacky workaround for 'self' --- mypy/renaming.py | 3 ++- test-data/unit/check-redefine.test | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/mypy/renaming.py b/mypy/renaming.py index 249b879b760f..b81e58d6d79c 100644 --- a/mypy/renaming.py +++ b/mypy/renaming.py @@ -85,7 +85,8 @@ def visit_func_def(self, fdef: FuncDef) -> None: for arg in fdef.arguments: name = arg.variable.name() - self.record_assignment(arg.variable.name(), can_be_redefined=True) + can_be_redefined = name != 'self' # TODO: Proper check + self.record_assignment(arg.variable.name(), can_be_redefined) self.handle_arg(name) for stmt in fdef.body.body: diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index e2670f9f4bd7..81de60b433eb 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -305,6 +305,19 @@ def f() -> None: # E: Incompatible types in assignment (expression has type "int", variable has type "str") +[case testCannotRedefineSelf] +class A: + x = 0 + + def f(self) -> None: + reveal_type(self.x) # E: Revealed type is 'builtins.int' + self = f() + self.y: str = '' + reveal_type(self.y) # E: Revealed type is 'builtins.str' + +def f() -> A: return A() + + -- Redefine global variable -- ------------------------ From b57960b4143ce248427c6871b835f39404100dfa Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 10 Oct 2018 15:30:37 +0100 Subject: [PATCH 24/47] Remove unused things --- mypy/semanal.py | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 2ddd9c4f1521..45a19f4cc230 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1695,11 +1695,9 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: s.rvalue.accept(self) - has_initializer = not isinstance(s.rvalue, TempNode) for lval in s.lvalues: self.analyze_lvalue(lval, explicit_type=s.type is not None, - is_final=s.is_final_def, - has_initializer=has_initializer) + is_final=s.is_final_def) self.check_final_implicit_def(s) self.check_classvar(s) if s.type: @@ -1972,8 +1970,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> None: def analyze_lvalue(self, lval: Lvalue, nested: bool = False, add_global: bool = False, explicit_type: bool = False, - is_final: bool = False, - has_initializer: bool = True) -> None: + is_final: bool = False) -> None: """Analyze an lvalue or assignment target. Note that this is used in both pass 1 and 2. @@ -1983,10 +1980,9 @@ def analyze_lvalue(self, lval: Lvalue, nested: bool = False, nested: If true, the lvalue is within a tuple or list lvalue expression add_global: Add name to globals table only if this is true (used in first pass) explicit_type: Assignment has type annotation - has_initializer: Is there a rvalue (i.e. not just "x: t")? """ if isinstance(lval, NameExpr): - self.analyze_name_lvalue(lval, add_global, explicit_type, is_final, has_initializer) + self.analyze_name_lvalue(lval, add_global, explicit_type, is_final) elif isinstance(lval, MemberExpr): if not add_global: self.analyze_member_lvalue(lval, explicit_type, is_final) @@ -2015,8 +2011,7 @@ def analyze_name_lvalue(self, lval: NameExpr, add_global: bool, explicit_type: bool, - is_final: bool, - has_initializer: bool) -> None: + is_final: bool) -> None: """Analyze an lvalue that targets a name expression. Arguments are similar to "analyze_lvalue". @@ -2046,8 +2041,7 @@ def analyze_name_lvalue(self, and unmangle(lval.name) + "'" != lval.name): self.fail("Cannot redefine an existing name as final", lval) assert lval.node.name() in self.globals or self.cur_mod_id == 'typing' - elif (self.locals[-1] is not None - and lval.name not in self.locals[-1] + elif (self.locals[-1] is not None and lval.name not in self.locals[-1] and lval.name not in self.global_decls[-1] and lval.name not in self.nonlocal_decls[-1]): # Define new local name. @@ -3565,17 +3559,11 @@ def add_symbol(self, name: str, node: SymbolTableNode, return self.globals[name] = node - def add_local(self, node: Union[Var, FuncDef, OverloadedFuncDef], ctx: Context, - allow_redefine: bool = False) -> None: - """Add local variable or function. - - Args: - allow_redefine: If true, override any existing local symbol with the same name - """ + def add_local(self, node: Union[Var, FuncDef, OverloadedFuncDef], ctx: Context) -> None: + """Add local variable or function.""" assert self.locals[-1] is not None, "Should not add locals outside a function" name = node.name() - if name in self.locals[-1] and (not allow_redefine or - not isinstance(self.locals[-1][name].node, Var)): + if name in self.locals[-1]: self.name_already_defined(name, ctx, self.locals[-1][name]) return node._fullname = name From 580a7742227c78a28749d4d136f9678da09ff596 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 10 Oct 2018 17:26:56 +0100 Subject: [PATCH 25/47] Revert some unnecessary changes --- mypy/semanal.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 45a19f4cc230..0ef8b2532619 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -374,8 +374,8 @@ def visit_func_def(self, defn: FuncDef) -> None: def _visit_func_def(self, defn: FuncDef) -> None: phase_info = self.postpone_nested_functions_stack[-1] if phase_info != FUNCTION_SECOND_PHASE: - # First phase of analysis for function. self.function_stack.append(defn) + # First phase of analysis for function. if not defn._fullname: defn._fullname = self.qualified_name(defn.name()) if defn.type: @@ -719,7 +719,7 @@ def analyze_function(self, defn: FuncItem) -> None: # First analyze body of the function but ignore nested functions. self.postpone_nested_functions_stack.append(FUNCTION_FIRST_PHASE_POSTPONE_SECOND) self.postponed_functions_stack.append([]) - self.visit_block(defn.body) + defn.body.accept(self) # Analyze nested functions (if any) as a second phase. self.postpone_nested_functions_stack[-1] = FUNCTION_SECOND_PHASE @@ -2041,9 +2041,9 @@ def analyze_name_lvalue(self, and unmangle(lval.name) + "'" != lval.name): self.fail("Cannot redefine an existing name as final", lval) assert lval.node.name() in self.globals or self.cur_mod_id == 'typing' - elif (self.locals[-1] is not None and lval.name not in self.locals[-1] - and lval.name not in self.global_decls[-1] - and lval.name not in self.nonlocal_decls[-1]): + elif (self.locals[-1] is not None and lval.name not in self.locals[-1] and + lval.name not in self.global_decls[-1] and + lval.name not in self.nonlocal_decls[-1]): # Define new local name. v = self.make_name_lvalue_var(lval, LDEF, not explicit_type) self.add_local(v, lval) From dc03143968bbf26ef539e56c9facfbc92470b4a7 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sun, 9 Dec 2018 11:45:16 +0000 Subject: [PATCH 26/47] Fix test case --- test-data/unit/check-literal.test | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index c7f35c94ec5e..689c025322f3 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -1169,14 +1169,15 @@ a: Literal[3] b: int c: Literal["foo"] -a = a * a # E: Incompatible types in assignment (expression has type "int", variable has type "Literal[3]") -a = a * b # E: Incompatible types in assignment (expression has type "int", variable has type "Literal[3]") -a = b * a # E: Incompatible types in assignment (expression has type "int", variable has type "Literal[3]") +if int(): + a = a * a # E: Incompatible types in assignment (expression has type "int", variable has type "Literal[3]") + a = a * b # E: Incompatible types in assignment (expression has type "int", variable has type "Literal[3]") + a = b * a # E: Incompatible types in assignment (expression has type "int", variable has type "Literal[3]") -b = a * a -b = a * b -b = b * a + b = a * a + b = a * b + b = b * a -c = c.strip() # E: Incompatible types in assignment (expression has type "str", variable has type "Literal['foo']") + c = c.strip() # E: Incompatible types in assignment (expression has type "str", variable has type "Literal['foo']") [builtins fixtures/ops.pyi] [out] From 20fd0f92271c4eeecef153a9bf076f988e4c7b91 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 20 Dec 2018 14:13:49 +0000 Subject: [PATCH 27/47] Use `if int():` consistently --- test-data/unit/check-basic.test | 8 +++--- test-data/unit/check-classes.test | 10 ++++---- test-data/unit/check-functions.test | 12 ++++----- test-data/unit/check-generics.test | 28 ++++++++++----------- test-data/unit/check-incremental.test | 6 ++--- test-data/unit/check-inference-context.test | 10 ++++---- test-data/unit/check-inference.test | 26 +++++++++---------- test-data/unit/check-isinstance.test | 2 +- test-data/unit/check-modules.test | 4 +-- test-data/unit/check-namedtuple.test | 6 ++--- test-data/unit/check-optional.test | 2 +- test-data/unit/check-overloading.test | 12 ++++----- test-data/unit/check-statements.test | 2 +- test-data/unit/check-super.test | 2 +- test-data/unit/check-type-checks.test | 6 ++--- test-data/unit/check-typeddict.test | 2 +- test-data/unit/check-typevar-values.test | 6 ++--- test-data/unit/check-unions.test | 4 +-- test-data/unit/check-unreachable-code.test | 2 +- 19 files changed, 75 insertions(+), 75 deletions(-) diff --git a/test-data/unit/check-basic.test b/test-data/unit/check-basic.test index c40e9d75224a..629d6bf291d2 100644 --- a/test-data/unit/check-basic.test +++ b/test-data/unit/check-basic.test @@ -125,7 +125,7 @@ main:4: error: Too many arguments for "f" def f() -> None: x = None # type: A y = None # type: B - if 1: + if int(): x = x x = y # E: Incompatible types in assignment (expression has type "B", variable has type "A") class A: pass @@ -144,7 +144,7 @@ class B: pass [case testFunctionArguments] import typing def f(x: 'A', y: 'B') -> None: - if 1: + if int(): x = y # E: Incompatible types in assignment (expression has type "B", variable has type "A") x = x y = B() @@ -239,12 +239,12 @@ a = __file__ # type: int # E: Incompatible types in assignment (expression has [case testLocalVariableShadowing] a = None # type: A -if 1: +if int(): a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") a = A() def f() -> None: a = None # type: B - if 1: + if int(): a = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") a = B() a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 10bf6333c861..ef439ec57155 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -73,7 +73,7 @@ main:4: error: Incompatible types in assignment (expression has type "object", v import typing class A: def f(self, a: 'A', b: 'B') -> None: - if 1: + if int(): a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") a = A() @@ -952,7 +952,7 @@ class A: class B: def __init__(self, a: 'A') -> None: pass b = B(A()) - if 1: + if int(): b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") b = B() # E: Too few arguments for "B" [out] @@ -962,7 +962,7 @@ class A: def f() -> None: class A: pass a = None # type: A - if 1: + if int(): a = A() a = object() # E: Incompatible types in assignment (expression has type "object", variable has type "A") [out] @@ -3114,7 +3114,7 @@ def new(uc: Type[U]) -> U: uc.foo() u = uc() u.foo() - if 1: + if int(): u = uc(0) u.foo() u = uc('') # Error @@ -4403,7 +4403,7 @@ def parse_ast(name_dict: NameDict) -> None: pass x = name_dict[''] reveal_type(x) # E: Revealed type is '__main__.NameInfo*' - if 1: + if int(): x = NameInfo(Base()) # OK x = Base() # E: Incompatible types in assignment (expression has type "Base", variable has type "NameInfo") [builtins fixtures/isinstancelist.pyi] diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index cad0fdd47086..5bebc988d16f 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -1286,7 +1286,7 @@ from typing import Any x = None # type: Any if x: def f(x: int) -> None: - if 1: + if int(): x = 1 x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") [out] @@ -1307,12 +1307,12 @@ x = None # type: Any if x: def f(x: int) -> None: 'x' + x # fail - if 1: + if int(): x = 1 else: def f(x: int) -> None: x + 'x' # fail - if 1: + if int(): x = 1 f(1) f('x') # fail @@ -1533,7 +1533,7 @@ x = None # type: Any class A: if x: def f(self, x: int) -> None: - if 1: + if int(): x = 1 x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") [out] @@ -1566,12 +1566,12 @@ class A: if x: def f(self, x: int) -> None: 'x' + x # fail - if 1: + if int(): x = 1 else: def f(self, x: int) -> None: x + 'x' # fail - if 1: + if int(): x = 1 A().f(1) A().f('x') # fail diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index d599ce03088a..478a7ef9023d 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -214,13 +214,13 @@ class A(Generic[S, T]): def f(self) -> None: s = None # type: S t = None # type: T - if 1: + if int(): s = t # E: Incompatible types in assignment (expression has type "T", variable has type "S") t = s # E: Incompatible types in assignment (expression has type "S", variable has type "T") a = self # type: A[S, B] # E: Incompatible types in assignment (expression has type "A[S, T]", variable has type "A[S, B]") b = self # type: A[T, T] # E: Incompatible types in assignment (expression has type "A[S, T]", variable has type "A[T, T]") c = self # type: A[S, T] - if 1: + if int(): t = t class B: pass @@ -241,10 +241,10 @@ T = TypeVar('T') class A(Generic[T]): def f(self) -> T: a = object() # type: T # E: Incompatible types in assignment (expression has type "object", variable has type "T") - if 1: + if int(): a = object() # E: Incompatible types in assignment (expression has type "object", variable has type "T") b = self.f() # type: object - if 1: + if int(): b = self.f() return None [out] @@ -372,7 +372,7 @@ class p(Generic[T, S]): def __init__(self, t: T, a: S) -> None: pass def f(s: S, t: T) -> p[T, A]: a = t # type: S # E: Incompatible types in assignment (expression has type "T", variable has type "S") - if 1: + if int(): s = t # E: Incompatible types in assignment (expression has type "T", variable has type "S") p_s_a = None # type: p[S, A] if s: @@ -391,7 +391,7 @@ class p(Generic[T, S]): def __init__(self, t: T, a: S) -> None: pass class A(Generic[T]): def f(self, s: S, t: T) -> p[S, T]: - if 1: + if int(): s = t # E: Incompatible types in assignment (expression has type "T", variable has type "S") p_s_s = None # type: p[S, S] if s: @@ -399,7 +399,7 @@ class A(Generic[T]): p_t_t = None # type: p[T, T] if t: return p_t_t # E: Incompatible return value type (got "p[T, T]", expected "p[S, T]") - if 1: + if int(): t = t s = s p_s_t = None # type: p[S, T] @@ -759,7 +759,7 @@ if not isinstance(x, int): x.x = 'a' # E: Incompatible types in assignment (expression has type "str", variable has type "int") def f(x: T) -> UNode[T]: - if 1: + if int(): return Node(x) else: return 1 @@ -1440,7 +1440,7 @@ def f() -> None: T = TypeVar('T') def g(x: T) -> T: pass a = g(1) - if 1: + if int(): a = 1 a = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") [out] @@ -1559,32 +1559,32 @@ def outer(t: T) -> None: def f5(x: T) -> T: ... y1 = f1 - if 1: + if int(): y1 = f2 y1 = f3 # E: Incompatible types in assignment (expression has type "Callable[[T], A]", variable has type "Callable[[A], A]") y1 = f4 # E: Incompatible types in assignment (expression has type "Callable[[A], T]", variable has type "Callable[[A], A]") y1 = f5 # E: Incompatible types in assignment (expression has type "Callable[[T], T]", variable has type "Callable[[A], A]") y2 = f2 - if 1: + if int(): y2 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[A], B]") y3 = f3 - if 1: + if int(): y3 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[T], A]") y3 = f2 y3 = f4 # E: Incompatible types in assignment (expression has type "Callable[[A], T]", variable has type "Callable[[T], A]") y3 = f5 # E: Incompatible types in assignment (expression has type "Callable[[T], T]", variable has type "Callable[[T], A]") y4 = f4 - if 1: + if int(): y4 = f1 # E: Incompatible types in assignment (expression has type "Callable[[A], A]", variable has type "Callable[[A], T]") y4 = f2 y4 = f3 # E: Incompatible types in assignment (expression has type "Callable[[T], A]", variable has type "Callable[[A], T]") y4 = f5 # E: Incompatible types in assignment (expression has type "Callable[[T], T]", variable has type "Callable[[A], T]") y5 = f5 - if 1: + if int(): y5 = f1 y5 = f2 y5 = f3 diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index b3e70a12f70a..f3f06e3db060 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -1340,7 +1340,7 @@ from mod4 import C class B: def makeC(self) -> C: val = 3 # type: int - if 1: + if int(): val = "str" # deliberately triggering error return C() @@ -1380,7 +1380,7 @@ from mod4 import C class B: def makeC(self) -> C: val = 3 # type: int - if 1: + if int(): val = "str" # deliberately triggering error return C() @@ -1420,7 +1420,7 @@ from mod4 import C class B: def makeC(self) -> C: val = 3 # type: int - if 1: + if int(): val = "str" # deliberately triggering error return C() diff --git a/test-data/unit/check-inference-context.test b/test-data/unit/check-inference-context.test index df2e9b42ce5d..d075d439cce6 100644 --- a/test-data/unit/check-inference-context.test +++ b/test-data/unit/check-inference-context.test @@ -86,11 +86,11 @@ def g() -> None: b = None # type: B x = f(o) - if 1: + if int(): ab = x # E: Incompatible types in assignment (expression has type "A[object]", variable has type "A[B]") ao = x y = f(b) - if 1: + if int(): ao = y # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") ab = y @@ -118,7 +118,7 @@ def g() -> None: ab = None # type: A[B] b = None # type: B x, y = f(b), f(b) - if 1: + if int(): ao = x # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") ao = y # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") ab = x @@ -137,7 +137,7 @@ def h() -> None: ab = None # type: A[B] b = None # type: B x, y = g(f(b)) - if 1: + if int(): ao = x # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") ao = y # E: Incompatible types in assignment (expression has type "A[B]", variable has type "A[object]") ab = x @@ -428,7 +428,7 @@ def f() -> None: a = [] # E: Need type annotation for 'a' b = [None] c = [B()] - if 1: + if int(): c = [object()] # E: List item 0 has incompatible type "object"; expected "B" c = [B()] class B: pass diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 2ee71858fe44..fd411e1e0f2b 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -22,7 +22,7 @@ import typing def f() -> None: x = A() y = B() - if 1: + if int(): x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") x = A() x = y # E: Incompatible types in assignment (expression has type "B", variable has type "A") @@ -44,7 +44,7 @@ def g() -> None: pass import typing def f(a: 'A') -> None: b = a - if 1: + if int(): b = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") b = a a = b @@ -59,7 +59,7 @@ g = None # type: B def f() -> None: a = g - if 1: + if int(): a = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") a = B() @@ -98,7 +98,7 @@ def f() -> None: a = A(), B() aa = None # type: A bb = None # type: B - if 1: + if int(): bb = a[0] # E: Incompatible types in assignment (expression has type "A", variable has type "B") aa = a[1] # E: Incompatible types in assignment (expression has type "B", variable has type "A") aa = a[0] @@ -129,7 +129,7 @@ a_s = None # type: A[str] def f() -> None: a_int = A() # type: A[int] a = a_int - if 1: + if int(): a = a_s # E: Incompatible types in assignment (expression has type "A[str]", variable has type "A[int]") a = a_i [builtins fixtures/tuple.pyi] @@ -168,7 +168,7 @@ class A: pass import typing def f() -> None: a, b = A(), B() - if 1: + if int(): a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") @@ -185,7 +185,7 @@ from typing import Tuple def f() -> None: t = None # type: Tuple[A, B] a, b = t - if 1: + if int(): a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") @@ -202,7 +202,7 @@ from typing import Tuple def f() -> None: t = None # type: Tuple[A, B] a1, (a, b) = A(), t - if 1: + if int(): a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") @@ -218,7 +218,7 @@ class B: pass import typing def f() -> None: a, (b, c) = A(), (B(), C()) - if 1: + if int(): a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") @@ -237,7 +237,7 @@ class C: pass import typing def f() -> None: a, (b, c) = A(), [B(), C()] - if 1: + if int(): a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") b = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") @@ -311,7 +311,7 @@ def f() -> None: list_d = [D()] a, b = list_c c, d, e = list_d - if 1: + if int(): a = D() # E: Incompatible types in assignment (expression has type "D", variable has type "C") b = D() # E: Incompatible types in assignment (expression has type "D", variable has type "C") c = C() # E: Incompatible types in assignment (expression has type "C", variable has type "D") @@ -340,7 +340,7 @@ def f() -> None: list_d = [D()] c1, (a, b) = C(), list_c c2, (c, d, e) = C(), list_d - if 1: + if int(): a = D() # E: Incompatible types in assignment (expression has type "D", variable has type "C") b = D() # E: Incompatible types in assignment (expression has type "D", variable has type "C") c = C() # E: Incompatible types in assignment (expression has type "C", variable has type "D") @@ -423,7 +423,7 @@ def f() -> None: a = id b = None # type: int c = None # type: str - if 1: + if int(): b = a(c) # E: Incompatible types in assignment (expression has type "str", variable has type "int") b = a(b) c = a(c) diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index dcdf2b92097f..3810c4258c83 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -136,7 +136,7 @@ if int(): from typing import Union def f(x: Union[int, str]) -> None: - if 1: # Without this, the assignment below would create a new variable "x" of type "int" + if int(): # Without this, the assignment below would create a new variable "x" of type "int" x = 1 if x: x = 'a' diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 02ffc9a92dee..9e4a61882cda 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -81,7 +81,7 @@ def f() -> None: pass import typing def f() -> None: from m import a, b, f, A, B - if 1: + if int(): a = b \ # E: Incompatible types in assignment (expression has type "B", variable has type "A") a = a @@ -103,7 +103,7 @@ import typing class C: def f(self) -> None: from m import * - if 1: + if int(): a = b # E: Incompatible types in assignment (expression has type "B", variable has type "A") a = a f() diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 34437d6ffb41..c5b24e99c305 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -231,7 +231,7 @@ class B(A): self.f(self.b) # E: Argument 1 to "f" of "B" has incompatible type "str"; expected "int" i = 0 s = '' - if 1: + if int(): i, s = self i, i = self # E: Incompatible types in assignment (expression has type "str", \ variable has type "int") @@ -246,7 +246,7 @@ class B(A): def f(self, x: 'B') -> None: i = 0 s = '' - if 1: + if int(): self = x i, s = x i, s = x.a, x.b @@ -565,7 +565,7 @@ import a def f(x: a.N) -> None: reveal_type(x) - if 1: + if int(): x = a.N(1) reveal_type(x) [out] diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index 195ec59d686a..2e515a1669b8 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -745,7 +745,7 @@ def g(x: Optional[int]) -> int: if x is not None: return x reveal_type(x) # E: Revealed type is 'None' - if 1: + if int(): x = f() reveal_type(x) # E: Revealed type is 'Union[builtins.int, Any]' return x diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 0323460aa9c3..1284704d0ca2 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -186,7 +186,7 @@ def f(x: 'B') -> 'A': ... def f(x: Any) -> Any: foo = 1 - if 1: + if int(): foo = "bar" # E: Incompatible types in assignment (expression has type "str", variable has type "int") @overload @@ -196,7 +196,7 @@ def g(x: 'B') -> 'A': ... def g(x): foo = 1 - if 1: + if int(): foo = "bar" reveal_type(f(A())) # E: Revealed type is '__main__.B' @@ -400,12 +400,12 @@ from foo import * from typing import overload @overload def f(x: 'A'): - if 1: + if int(): x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") x = A() @overload def f(x: 'B'): - if 1: + if int(): x = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") x = B() class A: pass @@ -419,12 +419,12 @@ from typing import overload class A: @overload def f(self, x: 'A'): - if 1: + if int(): x = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A") x = A() @overload def f(self, x: 'B'): - if 1: + if int(): x = A() # E: Incompatible types in assignment (expression has type "A", variable has type "B") x = B() class B: pass diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index d57e79ac5ce8..50d10872c609 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -1438,7 +1438,7 @@ import typing def f() -> None: x = 1 y = 'x' - if 1: + if int(): x = y = 'x' # E: Incompatible types in assignment (expression has type "str", variable has type "int") x = y = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "str") [builtins fixtures/primitives.pyi] diff --git a/test-data/unit/check-super.test b/test-data/unit/check-super.test index b50c8242f57a..89fd9b4504fb 100644 --- a/test-data/unit/check-super.test +++ b/test-data/unit/check-super.test @@ -12,7 +12,7 @@ class B: class A(B): def f(self) -> 'A': a, b = None, None # type: (A, B) - if 1: + if int(): a = super().f() # E: Incompatible types in assignment (expression has type "B", variable has type "A") a = super().g() # E: "g" undefined in superclass b = super().f() diff --git a/test-data/unit/check-type-checks.test b/test-data/unit/check-type-checks.test index 483c8c5a3b79..106f2d680ba4 100644 --- a/test-data/unit/check-type-checks.test +++ b/test-data/unit/check-type-checks.test @@ -17,7 +17,7 @@ if int(): [case testSimpleIsinstance2] import typing def f(x: object, n: int, s: str) -> None: - if 1: + if int(): n = x # E: Incompatible types in assignment (expression has type "object", variable has type "int") if isinstance(x, int): n = x @@ -75,7 +75,7 @@ def f(x: object, y: object, n: int, s: str) -> None: [case testIsinstanceAndElif] import typing def f(x: object, n: int, s: str) -> None: - if 1: + if int(): n = x # E: Incompatible types in assignment (expression has type "object", variable has type "int") if isinstance(x, int): n = x @@ -93,7 +93,7 @@ def f(x: object, n: int, s: str) -> None: [case testIsinstanceAndAnyType] from typing import Any def f(x: Any, n: int, s: str) -> None: - if 1: + if int(): s = x if isinstance(x, int): n = x diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index a01ee8fa8d0d..f566c9f36dd0 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -809,7 +809,7 @@ from mypy_extensions import TypedDict Point = TypedDict('Point', {'x': int, 'y': int}) def f(p: Point) -> None: - if 1: + if int(): p = {'x': 2, 'y': 3} p = {'x': 2} # E: Key 'y' missing for TypedDict "Point" p = dict(x=2, y=3) diff --git a/test-data/unit/check-typevar-values.test b/test-data/unit/check-typevar-values.test index 7924655dca72..7bd36677febe 100644 --- a/test-data/unit/check-typevar-values.test +++ b/test-data/unit/check-typevar-values.test @@ -96,7 +96,7 @@ T = TypeVar('T', int, str) def f(x: T) -> T: a = None # type: T b = None # type: T - if 1: + if int(): a = x b = x a = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") @@ -418,7 +418,7 @@ class C(Generic[Y]): def f(x: X, y: Y, z: int) -> None: C(y) C(x) # Error - if 1: + if int(): z = x # Error z = y # Error y.foo # Error @@ -502,7 +502,7 @@ from typing import TypeVar, overload, Callable T = TypeVar('T', int, str) def f(x: T) -> None: y = m(g, x) - if 1: + if int(): x = y y = object() # Error diff --git a/test-data/unit/check-unions.test b/test-data/unit/check-unions.test index 7064bde69b3f..da90bc6dcd2e 100644 --- a/test-data/unit/check-unions.test +++ b/test-data/unit/check-unions.test @@ -27,11 +27,11 @@ from typing import Union def f(x: Union[int, str]) -> None: if isinstance(x, int): y = 1 - if 1: + if int(): y = x else: z = 2 - if 1: + if int(): z = x # E: Incompatible types in assignment (expression has type "str", variable has type "int") [builtins fixtures/isinstance.pyi] [out] diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index 31e516ad8add..3e27c1cf8426 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -637,7 +637,7 @@ class Child(Parent): return 3 def bar(self) -> int: - if 1: + if int(): self = super(Child, self).something() reveal_type(self) # E: Revealed type is '__main__.Child' if self is None: From 3843ceaf7f31494a912d751e600483c00e3c0754 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 20 Dec 2018 15:22:11 +0000 Subject: [PATCH 28/47] Fix some test cases --- test-data/unit/check-classes.test | 2 +- test-data/unit/check-generics.test | 2 +- test-data/unit/check-incremental.test | 6 +++--- test-data/unit/check-isinstance.test | 2 +- test-data/unit/check-optional.test | 2 +- test-data/unit/check-typevar-values.test | 2 +- test-data/unit/check-unreachable-code.test | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index ef439ec57155..e97ce38ae4b0 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -3114,7 +3114,7 @@ def new(uc: Type[U]) -> U: uc.foo() u = uc() u.foo() - if int(): + if 1: u = uc(0) u.foo() u = uc('') # Error diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 478a7ef9023d..3087352e9c03 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -399,7 +399,7 @@ class A(Generic[T]): p_t_t = None # type: p[T, T] if t: return p_t_t # E: Incompatible return value type (got "p[T, T]", expected "p[S, T]") - if int(): + if 1: t = t s = s p_s_t = None # type: p[S, T] diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index f3f06e3db060..b3e70a12f70a 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -1340,7 +1340,7 @@ from mod4 import C class B: def makeC(self) -> C: val = 3 # type: int - if int(): + if 1: val = "str" # deliberately triggering error return C() @@ -1380,7 +1380,7 @@ from mod4 import C class B: def makeC(self) -> C: val = 3 # type: int - if int(): + if 1: val = "str" # deliberately triggering error return C() @@ -1420,7 +1420,7 @@ from mod4 import C class B: def makeC(self) -> C: val = 3 # type: int - if int(): + if 1: val = "str" # deliberately triggering error return C() diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index 3810c4258c83..fc418015ed23 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -136,7 +136,7 @@ if int(): from typing import Union def f(x: Union[int, str]) -> None: - if int(): # Without this, the assignment below would create a new variable "x" of type "int" + if 1: # Without this, the assignment below could create a new variable "x" of type "int" x = 1 if x: x = 'a' diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index 2e515a1669b8..195ec59d686a 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -745,7 +745,7 @@ def g(x: Optional[int]) -> int: if x is not None: return x reveal_type(x) # E: Revealed type is 'None' - if int(): + if 1: x = f() reveal_type(x) # E: Revealed type is 'Union[builtins.int, Any]' return x diff --git a/test-data/unit/check-typevar-values.test b/test-data/unit/check-typevar-values.test index 7bd36677febe..9e9056845dfc 100644 --- a/test-data/unit/check-typevar-values.test +++ b/test-data/unit/check-typevar-values.test @@ -96,7 +96,7 @@ T = TypeVar('T', int, str) def f(x: T) -> T: a = None # type: T b = None # type: T - if int(): + if 1: a = x b = x a = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") diff --git a/test-data/unit/check-unreachable-code.test b/test-data/unit/check-unreachable-code.test index 3e27c1cf8426..31e516ad8add 100644 --- a/test-data/unit/check-unreachable-code.test +++ b/test-data/unit/check-unreachable-code.test @@ -637,7 +637,7 @@ class Child(Parent): return 3 def bar(self) -> int: - if int(): + if 1: self = super(Child, self).something() reveal_type(self) # E: Revealed type is '__main__.Child' if self is None: From 5cfaf9cf76c386abe7658371eedd72c2ad65f4da Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 20 Dec 2018 16:44:36 +0000 Subject: [PATCH 29/47] Add flag for allowing redefinitions --- mypy/main.py | 4 ++++ mypy/options.py | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/mypy/main.py b/mypy/main.py index 649ef1ab9369..26d2c91b1cd9 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -530,6 +530,10 @@ def add_invertible_flag(flag: str, help="Suppress toplevel errors caused by missing annotations", group=strictness_group) + add_invertible_flag('--allow-redefinitions', default=False, strict_flag=False, + help="Allow unconditional variable redefinition with a new type", + group=strictness_group) + incremental_group = parser.add_argument_group( title='Incremental mode', description="Adjust how mypy incrementally type checks and caches modules. " diff --git a/mypy/options.py b/mypy/options.py index f36ef9190e04..535e9084bf59 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -21,6 +21,7 @@ class BuildType: PER_MODULE_OPTIONS = { # Please keep this list sorted "allow_untyped_globals", + "allow_redefinitions", "always_false", "always_true", "check_untyped_defs", @@ -149,6 +150,10 @@ def __init__(self) -> None: # Suppress toplevel errors caused by missing annotations self.allow_untyped_globals = False + # Allow variable to be redefined with an arbitrary type in the same block + # and the same nesting level as the initialization + self.allow_redefinitions = False + # Variable names considered True self.always_true = [] # type: List[str] From 83ba85f73577cad800ac6baec0365c7f54b325aa Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 20 Dec 2018 16:51:19 +0000 Subject: [PATCH 30/47] Use the flag --- mypy/semanal_pass1.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mypy/semanal_pass1.py b/mypy/semanal_pass1.py index b25f8551a834..9b522595a2e8 100644 --- a/mypy/semanal_pass1.py +++ b/mypy/semanal_pass1.py @@ -62,8 +62,9 @@ def visit_file(self, file: MypyFile, fnam: str, mod_id: str, options: Options) - and these will get resolved in later phases of semantic analysis. """ - # Perform renaming across the AST - file.accept(VariableRenameVisitor()) + if options.allow_redefinitions: + # Perform renaming across the AST to allow variable redefinitions + file.accept(VariableRenameVisitor()) sem = self.sem self.sem.options = options # Needed because we sometimes call into it self.pyversion = options.python_version From 43a0362b37eac54e4d492ca6924a7585c5607102 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 20 Dec 2018 16:59:43 +0000 Subject: [PATCH 31/47] Fix tests --- test-data/unit/check-final.test | 32 ++++++++++++++++++++++++++++- test-data/unit/check-inference.test | 28 +++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index 17991ea95db9..369df31760eb 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -336,6 +336,7 @@ class C: -- Reassignments [case testFinalReassignModuleVar] +# flags: --allow-redefinition from typing import Final x: Final = 1 @@ -358,7 +359,36 @@ y: Final = 2 # E: Cannot redefine an existing name as final y = 3 # E: Cannot assign to final name "y" z: Final = 1 -z: Final = 2 # E: Name 'z' already defined on line 22 \ +z: Final = 2 # E: Name 'z' already defined on line 23 \ + # E: Cannot redefine an existing name as final +z = 3 # E: Cannot assign to final name "z" + +[case testFinalReassignModuleVar2] +# flags: --disallow-redefinition +from typing import Final + +x: Final = 1 +x +x = 2 # E: Cannot assign to final name "x" +def f() -> int: + global x + x = 3 # E: Cannot assign to final name "x" + return x + +x2: Final = 1 +x2 +def f2() -> None: + global x2 + x2 = 1 # E: Cannot assign to final name "x2" + +y = 1 # E: Cannot assign to final name "y" +y +y: Final = 2 # E: Name 'y' already defined on line 18 \ + # E: Cannot redefine an existing name as final +y = 3 # E: Cannot assign to final name "y" + +z: Final = 1 +z: Final = 2 # E: Name 'z' already defined on line 23 \ # E: Cannot redefine an existing name as final z = 3 # E: Cannot assign to final name "z" diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index fd411e1e0f2b..f7cfa96eaa48 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -1045,6 +1045,26 @@ class B: pass [builtins fixtures/for.pyi] [case testReusingInferredForIndex2] +# flags: --allow-redefinition +def f() -> None: + for a in [A()]: pass + a = A() + a + if int(): + a = B() \ + # E: Incompatible types in assignment (expression has type "B", variable has type "A") + for a in []: pass # E: Need type annotation for 'a' + a = A() + if int(): + a = B() \ + # E: Incompatible types in assignment (expression has type "B", variable has type "A") +class A: pass +class B: pass +[builtins fixtures/for.pyi] +[out] + +[case testReusingInferredForIndex3] +# flags: --disallow-redefinition def f() -> None: for a in [A()]: pass a = A() @@ -2434,6 +2454,14 @@ _ = 0 _ = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") [case testUnusedTargetNotClass] +# flags: allow-redefinition +class C: + _, _ = 0, 0 + _ = '' +reveal_type(C._) # E: Revealed type is 'builtins.str' + +[case testUnusedTargetNotClass2] +# flags: disallow-redefinition class C: _, _ = 0, 0 _ = '' From 59b34135a6e31576685919e862b21fe86b9a313e Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 20 Dec 2018 18:12:28 +0000 Subject: [PATCH 32/47] Fix test cases --- test-data/unit/check-inference.test | 10 +++---- test-data/unit/check-redefine.test | 45 ++++++++++++++++++++++++----- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index f7cfa96eaa48..78e1914099b4 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -1072,7 +1072,7 @@ def f() -> None: if int(): a = B() \ # E: Incompatible types in assignment (expression has type "B", variable has type "A") - for a in []: pass # E: Need type annotation for 'a' + for a in []: pass a = A() if int(): a = B() \ @@ -2454,18 +2454,18 @@ _ = 0 _ = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") [case testUnusedTargetNotClass] -# flags: allow-redefinition +# flags: --allow-redefinition class C: _, _ = 0, 0 _ = '' reveal_type(C._) # E: Revealed type is 'builtins.str' [case testUnusedTargetNotClass2] -# flags: disallow-redefinition +# flags: --disallow-redefinition class C: _, _ = 0, 0 - _ = '' -reveal_type(C._) # E: Revealed type is 'builtins.str' + _ = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +reveal_type(C._) # E: Revealed type is 'builtins.int' [case testUnusedTargetTupleUnpacking] def foo() -> None: diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index 81de60b433eb..595736e4642f 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -6,6 +6,7 @@ [case testRedefineLocalWithDifferentType] +# flags: --allow-redefinitions def f() -> None: x = 0 reveal_type(x) # E: Revealed type is 'builtins.int' @@ -13,6 +14,7 @@ def f() -> None: reveal_type(x) # E: Revealed type is 'builtins.str' [case testCannotConditionallyRedefineLocalWithDifferentType] +# flags: --allow-redefinitions def f() -> None: y = 0 reveal_type(y) # E: Revealed type is 'builtins.int' @@ -23,6 +25,7 @@ def f() -> None: reveal_type(y) # E: Revealed type is 'builtins.int' [case testRedefineFunctionArg] +# flags: --allow-redefinitions def f(x: int) -> None: g(x) x = '' @@ -33,6 +36,7 @@ def g(x: int) -> None: reveal_type(x) # E: Revealed type is 'builtins.int' [case testRedefineAnnotationOnly] +# flags: --allow-redefinitions def f() -> None: x: int x = '' \ @@ -46,6 +50,7 @@ def g() -> None: reveal_type(x) # E: Revealed type is 'builtins.str' [case testRedefineLocalUsingOldValue] +# flags: --allow-redefinitions from typing import TypeVar, Union T = TypeVar('T') @@ -60,6 +65,7 @@ def f(x: int) -> None: def g(x: T) -> Union[T, str]: pass [case testRedefineLocalForLoopIndexVariable] +# flags: --allow-redefinitions from typing import Iterable def f(a: Iterable[int], b: Iterable[str]) -> None: for x in a: @@ -81,6 +87,7 @@ def h(a: Iterable[int]) -> None: for x in a: pass [case testCannotRedefineLocalWithinTry] +# flags: --allow-redefinitions def f() -> None: try: x = 0 @@ -98,6 +105,7 @@ def f() -> None: def g(): pass [case testCannotRedefineLocalWithinWith] +# flags: --allow-redefinitions def f() -> None: with g(): x = 0 @@ -113,6 +121,7 @@ def f() -> None: def g(): pass [case testCannotRedefineAcrossNestedFunction] +# flags: --allow-redefinitions def f() -> None: x = 0 x @@ -127,6 +136,7 @@ def f() -> None: y = '' [case testCannotRedefineAcrossNestedDecoratedFunction] +# flags: --allow-redefinitions def dec(f): return f def f() -> None: @@ -144,6 +154,7 @@ def f() -> None: y = '' [case testCannotRedefineAcrossNestedOverloadedFunction] +# flags: --allow-redefinitions from typing import overload def f() -> None: @@ -164,6 +175,7 @@ def f() -> None: y = '' [case testRedefineLocalInMultipleAssignment] +# flags: --allow-redefinitions def f() -> None: x, x = 1, '' reveal_type(x) # E: Revealed type is 'builtins.str' @@ -177,6 +189,7 @@ def g() -> None: # E: Incompatible types in assignment (expression has type "str", variable has type "int") [case testRedefineUnderscore] +# flags: --allow-redefinitions def f() -> None: _, _ = 1, '' if 1: @@ -184,6 +197,7 @@ def f() -> None: reveal_type(_) # E: Revealed type is 'Any' [case testRedefineWithBreakAndContinue] +# flags: --allow-redefinitions def f() -> None: y = 0 y @@ -217,6 +231,7 @@ def g() -> None: def h(): pass [case testRedefineLocalAndNestedLoops] +# flags: --allow-redefinitions def f() -> None: z = 0 z @@ -234,22 +249,25 @@ def f() -> None: z = '' [case testCannotRedefineVarAsFunction] +# flags: --allow-redefinitions def f() -> None: def x(): pass x = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Callable[[], Any]") reveal_type(x) # E: Revealed type is 'def () -> Any' y = 1 - def y(): pass # E: Name 'y' already defined on line 5 + def y(): pass # E: Name 'y' already defined on line 6 [case testCannotRedefineVarAsClass] +# flags: --allow-redefinitions def f() -> None: class x: pass x = 1 # E: Cannot assign to a type \ # E: Incompatible types in assignment (expression has type "int", variable has type "Type[x]") y = 1 - class y: pass # E: Name 'y' already defined on line 4 + class y: pass # E: Name 'y' already defined on line 5 [case testRedefineVarAsTypeVar] +# flags: --allow-redefinitions from typing import TypeVar def f() -> None: x = TypeVar('x') @@ -262,14 +280,16 @@ def f() -> None: def h(a: y) -> y: return a # E: Invalid type "y" [case testCannotRedefineVarAsModule] +# flags: --allow-redefinitions def f() -> None: import typing as m m = 1 # E: Incompatible types in assignment (expression has type "int", variable has type Module) n = 1 - import typing as n # E: Name 'n' already defined on line 4 + import typing as n # E: Name 'n' already defined on line 5 [builtins fixtures/module.pyi] [case testRedefineLocalWithTypeAnnotation] +# flags: --allow-redefinitions def f() -> None: x = 1 reveal_type(x) # E: Revealed type is 'builtins.int' @@ -285,13 +305,14 @@ def h() -> None: x = 1 reveal_type(x) # E: Revealed type is 'builtins.int' x: object - x: object = '' # E: Name 'x' already defined on line 15 + x: object = '' # E: Name 'x' already defined on line 16 def farg(x: int) -> None: - x: str = '' # E: Name 'x' already defined on line 17 + x: str = '' # E: Name 'x' already defined on line 18 def farg2(x: int) -> None: x: str = x # E: Incompatible types in assignment (expression has type "int", variable has type "str") [case testRedefineLocalWithTypeAnnotationSpecialCases] +# flags: --allow-redefinitions def f() -> None: x: object x = 1 @@ -306,6 +327,7 @@ def f() -> None: [case testCannotRedefineSelf] +# flags: --allow-redefinitions class A: x = 0 @@ -323,6 +345,7 @@ def f() -> A: return A() [case testRedefineGlobalWithDifferentType] +# flags: --allow-redefinitions import m reveal_type(m.x) [file m.py] @@ -336,9 +359,10 @@ reveal_type(x) tmp/m.py:2: error: Revealed type is 'builtins.int' tmp/m.py:4: error: Revealed type is 'builtins.object' tmp/m.py:6: error: Revealed type is 'builtins.str' -main:2: error: Revealed type is 'builtins.str' +main:3: error: Revealed type is 'builtins.str' [case testRedefineGlobalForIndex] +# flags: --allow-redefinitions import m reveal_type(m.x) [file m.py] @@ -355,9 +379,10 @@ reveal_type(x) tmp/m.py:6: error: Revealed type is 'builtins.int*' tmp/m.py:8: error: Revealed type is 'builtins.str*' tmp/m.py:9: error: Revealed type is 'builtins.str*' -main:2: error: Revealed type is 'builtins.str*' +main:3: error: Revealed type is 'builtins.str*' [case testRedefineGlobalBasedOnPreviousValues] +# flags: --allow-redefinitions from typing import TypeVar, Iterable T = TypeVar('T') def f(x: T) -> Iterable[T]: pass @@ -366,6 +391,7 @@ a = f(a) reveal_type(a) # E: Revealed type is 'typing.Iterable[builtins.int*]' [case testRedefineGlobalWithSeparateDeclaration] +# flags: --allow-redefinitions x = '' reveal_type(x) # E: Revealed type is 'builtins.str' x: int @@ -378,6 +404,7 @@ if int(): x = object() [case testRedefineGlobalUsingForLoop] +# flags: --allow-redefinitions from typing import Iterable, TypeVar, Union T = TypeVar('T') def f(x: T) -> Iterable[Union[T, str]]: pass @@ -388,13 +415,15 @@ for x in f(x): reveal_type(x) # E: Revealed type is 'Union[builtins.int*, builtins.str]' [case testNoRedefinitionIfOnlyInitialized] +# flags: --allow-redefinitions --no-strict-optional x = None # type: int x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") x = object() # E: Incompatible types in assignment (expression has type "object", variable has type "int") x # Reference to variable x = '' -[case testXXX] +[case testNoRedefinitionIfNoValueAssigned] +# flags: --allow-redefinitions --no-strict-optional x: int x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") reveal_type(x) # E: Revealed type is 'builtins.int' From 7e3eff9a7bb4d5976883a8c1c1b78804ade8eadd Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 20 Dec 2018 18:21:13 +0000 Subject: [PATCH 33/47] Add test case for turning flag off explicitly --- test-data/unit/check-redefine.test | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index 595736e4642f..5f14e7c96af3 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -423,8 +423,26 @@ x # Reference to variable x = '' [case testNoRedefinitionIfNoValueAssigned] -# flags: --allow-redefinitions --no-strict-optional +# flags: --allow-redefinitions x: int x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") reveal_type(x) # E: Revealed type is 'builtins.int' x: object + +[case testNoRedefinitionIfExplicitlyDisallowed] +# flags: --disallow-redefinitions +x = 0 +x = 2 +x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +def f() -> None: + y = 0 + y = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +class C: + y = 0 + y = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") +def g() -> None: + # _ is a special case + _ = 0 + _ = '' + x, _ = 0, C() +[builtins fixtures/tuple.pyi] From afa1096c8684d50b9902d945f01abd6e6b2c6fca Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 20 Dec 2018 18:23:06 +0000 Subject: [PATCH 34/47] Rename flag to --[dis]allow-redefinition (no plural) --- mypy/main.py | 2 +- mypy/options.py | 4 +- mypy/semanal_pass1.py | 2 +- test-data/unit/check-redefine.test | 60 +++++++++++++++--------------- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index 26d2c91b1cd9..008e1108d6b5 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -530,7 +530,7 @@ def add_invertible_flag(flag: str, help="Suppress toplevel errors caused by missing annotations", group=strictness_group) - add_invertible_flag('--allow-redefinitions', default=False, strict_flag=False, + add_invertible_flag('--allow-redefinition', default=False, strict_flag=False, help="Allow unconditional variable redefinition with a new type", group=strictness_group) diff --git a/mypy/options.py b/mypy/options.py index 535e9084bf59..f063cf444c83 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -21,7 +21,7 @@ class BuildType: PER_MODULE_OPTIONS = { # Please keep this list sorted "allow_untyped_globals", - "allow_redefinitions", + "allow_redefinition", "always_false", "always_true", "check_untyped_defs", @@ -152,7 +152,7 @@ def __init__(self) -> None: # Allow variable to be redefined with an arbitrary type in the same block # and the same nesting level as the initialization - self.allow_redefinitions = False + self.allow_redefinition = False # Variable names considered True self.always_true = [] # type: List[str] diff --git a/mypy/semanal_pass1.py b/mypy/semanal_pass1.py index 9b522595a2e8..53e303c57ec1 100644 --- a/mypy/semanal_pass1.py +++ b/mypy/semanal_pass1.py @@ -62,7 +62,7 @@ def visit_file(self, file: MypyFile, fnam: str, mod_id: str, options: Options) - and these will get resolved in later phases of semantic analysis. """ - if options.allow_redefinitions: + if options.allow_redefinition: # Perform renaming across the AST to allow variable redefinitions file.accept(VariableRenameVisitor()) sem = self.sem diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index 5f14e7c96af3..dccfb3eb8773 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -6,7 +6,7 @@ [case testRedefineLocalWithDifferentType] -# flags: --allow-redefinitions +# flags: --allow-redefinition def f() -> None: x = 0 reveal_type(x) # E: Revealed type is 'builtins.int' @@ -14,7 +14,7 @@ def f() -> None: reveal_type(x) # E: Revealed type is 'builtins.str' [case testCannotConditionallyRedefineLocalWithDifferentType] -# flags: --allow-redefinitions +# flags: --allow-redefinition def f() -> None: y = 0 reveal_type(y) # E: Revealed type is 'builtins.int' @@ -25,7 +25,7 @@ def f() -> None: reveal_type(y) # E: Revealed type is 'builtins.int' [case testRedefineFunctionArg] -# flags: --allow-redefinitions +# flags: --allow-redefinition def f(x: int) -> None: g(x) x = '' @@ -36,7 +36,7 @@ def g(x: int) -> None: reveal_type(x) # E: Revealed type is 'builtins.int' [case testRedefineAnnotationOnly] -# flags: --allow-redefinitions +# flags: --allow-redefinition def f() -> None: x: int x = '' \ @@ -50,7 +50,7 @@ def g() -> None: reveal_type(x) # E: Revealed type is 'builtins.str' [case testRedefineLocalUsingOldValue] -# flags: --allow-redefinitions +# flags: --allow-redefinition from typing import TypeVar, Union T = TypeVar('T') @@ -65,7 +65,7 @@ def f(x: int) -> None: def g(x: T) -> Union[T, str]: pass [case testRedefineLocalForLoopIndexVariable] -# flags: --allow-redefinitions +# flags: --allow-redefinition from typing import Iterable def f(a: Iterable[int], b: Iterable[str]) -> None: for x in a: @@ -87,7 +87,7 @@ def h(a: Iterable[int]) -> None: for x in a: pass [case testCannotRedefineLocalWithinTry] -# flags: --allow-redefinitions +# flags: --allow-redefinition def f() -> None: try: x = 0 @@ -105,7 +105,7 @@ def f() -> None: def g(): pass [case testCannotRedefineLocalWithinWith] -# flags: --allow-redefinitions +# flags: --allow-redefinition def f() -> None: with g(): x = 0 @@ -121,7 +121,7 @@ def f() -> None: def g(): pass [case testCannotRedefineAcrossNestedFunction] -# flags: --allow-redefinitions +# flags: --allow-redefinition def f() -> None: x = 0 x @@ -136,7 +136,7 @@ def f() -> None: y = '' [case testCannotRedefineAcrossNestedDecoratedFunction] -# flags: --allow-redefinitions +# flags: --allow-redefinition def dec(f): return f def f() -> None: @@ -154,7 +154,7 @@ def f() -> None: y = '' [case testCannotRedefineAcrossNestedOverloadedFunction] -# flags: --allow-redefinitions +# flags: --allow-redefinition from typing import overload def f() -> None: @@ -175,7 +175,7 @@ def f() -> None: y = '' [case testRedefineLocalInMultipleAssignment] -# flags: --allow-redefinitions +# flags: --allow-redefinition def f() -> None: x, x = 1, '' reveal_type(x) # E: Revealed type is 'builtins.str' @@ -189,7 +189,7 @@ def g() -> None: # E: Incompatible types in assignment (expression has type "str", variable has type "int") [case testRedefineUnderscore] -# flags: --allow-redefinitions +# flags: --allow-redefinition def f() -> None: _, _ = 1, '' if 1: @@ -197,7 +197,7 @@ def f() -> None: reveal_type(_) # E: Revealed type is 'Any' [case testRedefineWithBreakAndContinue] -# flags: --allow-redefinitions +# flags: --allow-redefinition def f() -> None: y = 0 y @@ -231,7 +231,7 @@ def g() -> None: def h(): pass [case testRedefineLocalAndNestedLoops] -# flags: --allow-redefinitions +# flags: --allow-redefinition def f() -> None: z = 0 z @@ -249,7 +249,7 @@ def f() -> None: z = '' [case testCannotRedefineVarAsFunction] -# flags: --allow-redefinitions +# flags: --allow-redefinition def f() -> None: def x(): pass x = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Callable[[], Any]") @@ -258,7 +258,7 @@ def f() -> None: def y(): pass # E: Name 'y' already defined on line 6 [case testCannotRedefineVarAsClass] -# flags: --allow-redefinitions +# flags: --allow-redefinition def f() -> None: class x: pass x = 1 # E: Cannot assign to a type \ @@ -267,7 +267,7 @@ def f() -> None: class y: pass # E: Name 'y' already defined on line 5 [case testRedefineVarAsTypeVar] -# flags: --allow-redefinitions +# flags: --allow-redefinition from typing import TypeVar def f() -> None: x = TypeVar('x') @@ -280,7 +280,7 @@ def f() -> None: def h(a: y) -> y: return a # E: Invalid type "y" [case testCannotRedefineVarAsModule] -# flags: --allow-redefinitions +# flags: --allow-redefinition def f() -> None: import typing as m m = 1 # E: Incompatible types in assignment (expression has type "int", variable has type Module) @@ -289,7 +289,7 @@ def f() -> None: [builtins fixtures/module.pyi] [case testRedefineLocalWithTypeAnnotation] -# flags: --allow-redefinitions +# flags: --allow-redefinition def f() -> None: x = 1 reveal_type(x) # E: Revealed type is 'builtins.int' @@ -312,7 +312,7 @@ def farg2(x: int) -> None: x: str = x # E: Incompatible types in assignment (expression has type "int", variable has type "str") [case testRedefineLocalWithTypeAnnotationSpecialCases] -# flags: --allow-redefinitions +# flags: --allow-redefinition def f() -> None: x: object x = 1 @@ -327,7 +327,7 @@ def f() -> None: [case testCannotRedefineSelf] -# flags: --allow-redefinitions +# flags: --allow-redefinition class A: x = 0 @@ -345,7 +345,7 @@ def f() -> A: return A() [case testRedefineGlobalWithDifferentType] -# flags: --allow-redefinitions +# flags: --allow-redefinition import m reveal_type(m.x) [file m.py] @@ -362,7 +362,7 @@ tmp/m.py:6: error: Revealed type is 'builtins.str' main:3: error: Revealed type is 'builtins.str' [case testRedefineGlobalForIndex] -# flags: --allow-redefinitions +# flags: --allow-redefinition import m reveal_type(m.x) [file m.py] @@ -382,7 +382,7 @@ tmp/m.py:9: error: Revealed type is 'builtins.str*' main:3: error: Revealed type is 'builtins.str*' [case testRedefineGlobalBasedOnPreviousValues] -# flags: --allow-redefinitions +# flags: --allow-redefinition from typing import TypeVar, Iterable T = TypeVar('T') def f(x: T) -> Iterable[T]: pass @@ -391,7 +391,7 @@ a = f(a) reveal_type(a) # E: Revealed type is 'typing.Iterable[builtins.int*]' [case testRedefineGlobalWithSeparateDeclaration] -# flags: --allow-redefinitions +# flags: --allow-redefinition x = '' reveal_type(x) # E: Revealed type is 'builtins.str' x: int @@ -404,7 +404,7 @@ if int(): x = object() [case testRedefineGlobalUsingForLoop] -# flags: --allow-redefinitions +# flags: --allow-redefinition from typing import Iterable, TypeVar, Union T = TypeVar('T') def f(x: T) -> Iterable[Union[T, str]]: pass @@ -415,7 +415,7 @@ for x in f(x): reveal_type(x) # E: Revealed type is 'Union[builtins.int*, builtins.str]' [case testNoRedefinitionIfOnlyInitialized] -# flags: --allow-redefinitions --no-strict-optional +# flags: --allow-redefinition --no-strict-optional x = None # type: int x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") x = object() # E: Incompatible types in assignment (expression has type "object", variable has type "int") @@ -423,14 +423,14 @@ x # Reference to variable x = '' [case testNoRedefinitionIfNoValueAssigned] -# flags: --allow-redefinitions +# flags: --allow-redefinition x: int x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") reveal_type(x) # E: Revealed type is 'builtins.int' x: object [case testNoRedefinitionIfExplicitlyDisallowed] -# flags: --disallow-redefinitions +# flags: --disallow-redefinition x = 0 x = 2 x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") From 916288c27bd70c01f0706e92f2f33afdd9fb289a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 21 Dec 2018 12:50:47 +0000 Subject: [PATCH 35/47] Fix semantic analyzer tests Also allow flags to be specified for semantic analyzer tests. --- mypy/test/testsemanal.py | 14 +-- mypy/test/testtransform.py | 5 +- test-data/unit/semanal-basic.test | 11 +- test-data/unit/semanal-statements.test | 139 ++++++++++++++++--------- 4 files changed, 104 insertions(+), 65 deletions(-) diff --git a/mypy/test/testsemanal.py b/mypy/test/testsemanal.py index 096bdd4dacb2..e42a84e8365b 100644 --- a/mypy/test/testsemanal.py +++ b/mypy/test/testsemanal.py @@ -8,7 +8,7 @@ from mypy.modulefinder import BuildSource from mypy.defaults import PYTHON3_VERSION from mypy.test.helpers import ( - assert_string_arrays_equal, normalize_error_messages, testfile_pyversion, + assert_string_arrays_equal, normalize_error_messages, testfile_pyversion, parse_options ) from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.test.config import test_temp_dir @@ -35,8 +35,8 @@ 'semanal-python2.test'] -def get_semanal_options() -> Options: - options = Options() +def get_semanal_options(program_text: str, testcase: DataDrivenTestCase) -> Options: + options = parse_options(program_text, testcase, 1) options.use_builtins_fixtures = True options.semantic_analysis_only = True options.show_traceback = True @@ -61,7 +61,7 @@ def test_semanal(testcase: DataDrivenTestCase) -> None: try: src = '\n'.join(testcase.input) - options = get_semanal_options() + options = get_semanal_options(src, testcase) options.python_version = testfile_pyversion(testcase.file) result = build.build(sources=[BuildSource('main', None, src)], options=options, @@ -112,7 +112,7 @@ def test_semanal_error(testcase: DataDrivenTestCase) -> None: try: src = '\n'.join(testcase.input) res = build.build(sources=[BuildSource('main', None, src)], - options=get_semanal_options(), + options=get_semanal_options(src, testcase), alt_lib_path=test_temp_dir) a = res.errors assert a, 'No errors reported in {}, line {}'.format(testcase.file, testcase.line) @@ -139,7 +139,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: # Build test case input. src = '\n'.join(testcase.input) result = build.build(sources=[BuildSource('main', None, src)], - options=get_semanal_options(), + options=get_semanal_options(src, testcase), alt_lib_path=test_temp_dir) # The output is the symbol table converted into a string. a = result.errors @@ -169,7 +169,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: # Build test case input. src = '\n'.join(testcase.input) result = build.build(sources=[BuildSource('main', None, src)], - options=get_semanal_options(), + options=get_semanal_options(src, testcase), alt_lib_path=test_temp_dir) a = result.errors if a: diff --git a/mypy/test/testtransform.py b/mypy/test/testtransform.py index b4703f1906ac..014d0c17ceaf 100644 --- a/mypy/test/testtransform.py +++ b/mypy/test/testtransform.py @@ -5,7 +5,7 @@ from mypy import build from mypy.modulefinder import BuildSource from mypy.test.helpers import ( - assert_string_arrays_equal, testfile_pyversion, normalize_error_messages + assert_string_arrays_equal, testfile_pyversion, normalize_error_messages, parse_options ) from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.test.config import test_temp_dir @@ -36,11 +36,10 @@ def test_transform(testcase: DataDrivenTestCase) -> None: try: src = '\n'.join(testcase.input) - options = Options() + options = parse_options(src, testcase, 1) options.use_builtins_fixtures = True options.semantic_analysis_only = True options.show_traceback = True - options.python_version = testfile_pyversion(testcase.file) result = build.build(sources=[BuildSource('main', None, src)], options=options, alt_lib_path=test_temp_dir) diff --git a/test-data/unit/semanal-basic.test b/test-data/unit/semanal-basic.test index d7ef830eb2d7..22231f067de3 100644 --- a/test-data/unit/semanal-basic.test +++ b/test-data/unit/semanal-basic.test @@ -334,25 +334,26 @@ MypyFile:1( NameExpr(self [l])))))) [case testGlobalDefinedInBlock] +# flags: --allow-redefinition if object: x = object() x = x x [out] MypyFile:1( - IfStmt:1( + IfStmt:2( If( NameExpr(object [builtins.object])) Then( - AssignmentStmt:2( + AssignmentStmt:3( NameExpr(x'* [__main__.x']) - CallExpr:2( + CallExpr:3( NameExpr(object [builtins.object]) Args())) - AssignmentStmt:3( + AssignmentStmt:4( NameExpr(x* [__main__.x]) NameExpr(x' [__main__.x'])))) - ExpressionStmt:4( + ExpressionStmt:5( NameExpr(x [__main__.x]))) [case testNonlocalDecl] diff --git a/test-data/unit/semanal-statements.test b/test-data/unit/semanal-statements.test index a24c8ea52f55..682c1dfa0b89 100644 --- a/test-data/unit/semanal-statements.test +++ b/test-data/unit/semanal-statements.test @@ -157,24 +157,26 @@ MypyFile:1( NameExpr(x [l]))))) [case testReusingForLoopIndexVariable] +# flags: --allow-redefinition for x in None: pass for x in None: pass [out] MypyFile:1( - ForStmt:1( + ForStmt:2( NameExpr(x'* [__main__.x']) NameExpr(None [builtins.None]) - Block:1( - PassStmt:2())) - ForStmt:3( + Block:2( + PassStmt:3())) + ForStmt:4( NameExpr(x* [__main__.x]) NameExpr(None [builtins.None]) - Block:3( - PassStmt:4()))) + Block:4( + PassStmt:5()))) [case testReusingForLoopIndexVariable2] +# flags: --allow-redefinition def f(): for x in None: pass @@ -182,19 +184,19 @@ def f(): pass [out] MypyFile:1( - FuncDef:1( + FuncDef:2( f - Block:1( - ForStmt:2( + Block:2( + ForStmt:3( NameExpr(x* [l]) NameExpr(None [builtins.None]) - Block:2( - PassStmt:3())) - ForStmt:4( + Block:3( + PassStmt:4())) + ForStmt:5( NameExpr(x'* [l]) NameExpr(None [builtins.None]) - Block:4( - PassStmt:5()))))) + Block:5( + PassStmt:6()))))) [case testLoopWithElse] for x in []: @@ -353,47 +355,49 @@ MypyFile:1( IntExpr(1))) [case testStarLvalues] +# flags: --allow-redefinition *x, y = 1 *x, (y, *z) = 1 *(x, q), r = 1 [out] MypyFile:1( - AssignmentStmt:1( - TupleExpr:1( - StarExpr:1( - NameExpr(x'* [__main__.x'])) - NameExpr(y'* [__main__.y'])) - IntExpr(1)) AssignmentStmt:2( TupleExpr:2( StarExpr:2( - NameExpr(x''* [__main__.x''])) - TupleExpr:2( - NameExpr(y* [__main__.y]) - StarExpr:2( - NameExpr(z* [__main__.z])))) + NameExpr(x'* [__main__.x'])) + NameExpr(y'* [__main__.y'])) IntExpr(1)) AssignmentStmt:3( TupleExpr:3( StarExpr:3( - TupleExpr:3( + NameExpr(x''* [__main__.x''])) + TupleExpr:3( + NameExpr(y* [__main__.y]) + StarExpr:3( + NameExpr(z* [__main__.z])))) + IntExpr(1)) + AssignmentStmt:4( + TupleExpr:4( + StarExpr:4( + TupleExpr:4( NameExpr(x* [__main__.x]) NameExpr(q* [__main__.q]))) NameExpr(r* [__main__.r])) IntExpr(1))) [case testMultipleDefinition] +# flags: --allow-redefinition x, y = 1 x, y = 2 [out] MypyFile:1( - AssignmentStmt:1( - TupleExpr:1( + AssignmentStmt:2( + TupleExpr:2( NameExpr(x'* [__main__.x']) NameExpr(y'* [__main__.y'])) IntExpr(1)) - AssignmentStmt:2( - TupleExpr:2( + AssignmentStmt:3( + TupleExpr:3( NameExpr(x* [__main__.x]) NameExpr(y* [__main__.y])) IntExpr(2))) @@ -814,18 +818,19 @@ MypyFile:1( PassStmt:8())) [case testMultipleAssignmentWithPartialNewDef] +# flags: --allow-redefinition o = None x, o = o, o [out] MypyFile:1( - AssignmentStmt:1( + AssignmentStmt:2( NameExpr(o'* [__main__.o']) NameExpr(None [builtins.None])) - AssignmentStmt:2( - TupleExpr:2( + AssignmentStmt:3( + TupleExpr:3( NameExpr(x* [__main__.x]) NameExpr(o* [__main__.o])) - TupleExpr:2( + TupleExpr:3( NameExpr(o' [__main__.o']) NameExpr(o' [__main__.o'])))) @@ -939,6 +944,7 @@ MypyFile:1( NameExpr(b [l])))))))) [case testRenameGlobalVariable] +# flags: --allow-redefinition def f(a): pass x = 0 f(x) @@ -946,51 +952,84 @@ x = '' f(x) [out] MypyFile:1( - FuncDef:1( + FuncDef:2( f Args( Var(a)) - Block:1( - PassStmt:1())) - AssignmentStmt:2( + Block:2( + PassStmt:2())) + AssignmentStmt:3( NameExpr(x'* [__main__.x']) IntExpr(0)) - ExpressionStmt:3( - CallExpr:3( + ExpressionStmt:4( + CallExpr:4( NameExpr(f [__main__.f]) Args( NameExpr(x' [__main__.x'])))) - AssignmentStmt:4( + AssignmentStmt:5( + NameExpr(x* [__main__.x]) + StrExpr()) + ExpressionStmt:6( + CallExpr:6( + NameExpr(f [__main__.f]) + Args( + NameExpr(x [__main__.x]))))) + +[case testNoRenameGlobalVariable] +# flags: --disallow-redefinition +def f(a): pass +x = 0 +f(x) +x = '' +f(x) +[out] +MypyFile:1( + FuncDef:2( + f + Args( + Var(a)) + Block:2( + PassStmt:2())) + AssignmentStmt:3( NameExpr(x* [__main__.x]) + IntExpr(0)) + ExpressionStmt:4( + CallExpr:4( + NameExpr(f [__main__.f]) + Args( + NameExpr(x [__main__.x])))) + AssignmentStmt:5( + NameExpr(x [__main__.x]) StrExpr()) - ExpressionStmt:5( - CallExpr:5( + ExpressionStmt:6( + CallExpr:6( NameExpr(f [__main__.f]) Args( NameExpr(x [__main__.x]))))) [case testRenameLocalVariable] +# flags: --allow-redefinition def f(a): f(a) a = '' f(a) [out] MypyFile:1( - FuncDef:1( + FuncDef:2( f Args( Var(a)) - Block:1( - ExpressionStmt:2( - CallExpr:2( + Block:2( + ExpressionStmt:3( + CallExpr:3( NameExpr(f [__main__.f]) Args( NameExpr(a [l])))) - AssignmentStmt:3( + AssignmentStmt:4( NameExpr(a'* [l]) StrExpr()) - ExpressionStmt:4( - CallExpr:4( + ExpressionStmt:5( + CallExpr:5( NameExpr(f [__main__.f]) Args( NameExpr(a' [l]))))))) From 459187ea72e2eb4aea5bfeaec1ee6a655b5f037a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 21 Dec 2018 14:08:27 +0000 Subject: [PATCH 36/47] Update test case --- test-data/unit/check-redefine.test | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index dccfb3eb8773..9f64dbfc5324 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -422,6 +422,9 @@ x = object() # E: Incompatible types in assignment (expression has type "object" x # Reference to variable x = '' +y = 0 +y = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") + [case testNoRedefinitionIfNoValueAssigned] # flags: --allow-redefinition x: int From 76ad071d25108848007c63acc983c6b273ec5782 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 17 Jan 2019 14:26:34 +0000 Subject: [PATCH 37/47] Fix brokenness from merge --- mypy/semanal.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 3c17c77a7064..75097a866bbb 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1772,14 +1772,10 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: self.process__all__(s) def analyze_lvalues(self, s: AssignmentStmt) -> None: - def final_cb(keep_final: bool) -> None: - self.fail("Cannot redefine an existing name as final", s) - if not keep_final: - s.is_final_def = False - for lval in s.lvalues: - self.analyze_lvalue(lval, explicit_type=s.type is not None, - final_cb=final_cb if s.is_final_def else None) + self.analyze_lvalue(lval, + explicit_type=s.type is not None, + is_final=s.is_final_def) def apply_dynamic_class_hook(self, s: AssignmentStmt) -> None: if len(s.lvalues) > 1: From 979a17cbad292d07290b2af5d868984e969872db Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 18 Jan 2019 11:37:52 +0000 Subject: [PATCH 38/47] Respond to review --- mypy/renaming.py | 20 ++++++++++++++++---- mypy/semanal.py | 3 +-- mypy/semanal_shared.py | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/mypy/renaming.py b/mypy/renaming.py index b81e58d6d79c..74d2b74d90db 100644 --- a/mypy/renaming.py +++ b/mypy/renaming.py @@ -42,16 +42,18 @@ class VariableRenameVisitor(TraverserVisitor): def __init__(self) -> None: # Counter for labeling new blocks self.block_id = 0 + # Number of surrounding try statements that disallow variable redefinition self.disallow_redef_depth = 0 + # Number of surrounding loop statements self.loop_depth = 0 # Map block id to loop depth. self.block_loop_depth = {} # type: Dict[int, int] # Stack of block ids being processed. self.blocks = [] # type: List[int] - # List of scopes; each scope maps short name to block id. + # List of scopes; each scope maps short (unqualified) name to block id. self.var_blocks = [] # type: List[Dict[str, int]] - # References to variables the we may need to rename. List of + # References to variables that we may need to rename. List of # scopes; each scope is a mapping from name to list of collections # of names that refer to the same logical variable. self.refs = [] # type: List[Dict[str, List[List[NameExpr]]]] @@ -85,6 +87,8 @@ def visit_func_def(self, fdef: FuncDef) -> None: for arg in fdef.arguments: name = arg.variable.name() + # 'self' can't be redefined since it's special as it allows definition of + # attributes. 'cls' can't be used to define attributes so we can ignore it. can_be_redefined = name != 'self' # TODO: Proper check self.record_assignment(arg.variable.name(), can_be_redefined) self.handle_arg(name) @@ -146,6 +150,12 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None: self.analyze_lvalue(lvalue) def analyze_lvalue(self, lvalue: Lvalue, is_nested: bool = False) -> None: + """Process assignment; in particular, keep track of (re)defined names. + + Args: + is_nested: True for non-outermost Lvalue in a multiple assignment such as + "x, y = ..." + """ if isinstance(lvalue, NameExpr): name = lvalue.name is_new = self.record_assignment(name, True) @@ -154,8 +164,8 @@ def analyze_lvalue(self, lvalue: Lvalue, is_nested: bool = False) -> None: else: self.handle_refine(lvalue) if is_nested: - # This allows these to redefined freely. Multiple assignments often define - # dummy variables that are never read. + # This allows these to be redefined freely even if never read. Multiple + # assignment like "x, _ _ = y" defines dummy variables that are never read. self.handle_ref(lvalue) elif isinstance(lvalue, (ListExpr, TupleExpr)): for item in lvalue.items: @@ -166,6 +176,8 @@ def analyze_lvalue(self, lvalue: Lvalue, is_nested: bool = False) -> None: lvalue.base.accept(self) lvalue.index.accept(self) elif isinstance(lvalue, StarExpr): + # Propagate is_nested since in a typical use case like "x, *rest = ..." 'rest' may + # be freely reused. self.analyze_lvalue(lvalue.expr, is_nested=is_nested) def visit_name_expr(self, expr: NameExpr) -> None: diff --git a/mypy/semanal.py b/mypy/semanal.py index 75097a866bbb..b2ed59367599 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1752,8 +1752,7 @@ def add_type_alias_deps(self, aliases_used: Iterable[str], self.cur_mod_node.alias_deps[target].update(aliases_used) def visit_assignment_stmt(self, s: AssignmentStmt) -> None: - is_final = self.unwrap_final(s) - s.is_final_def = is_final + s.is_final_def = self.unwrap_final(s) self.analyze_lvalues(s) s.rvalue.accept(self) self.check_final_implicit_def(s) diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index d7408b25322d..4d8994e2084e 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -1,7 +1,7 @@ """Shared definitions used by different parts of semantic analysis.""" from abc import abstractmethod, abstractproperty -from typing import Optional, List, Callable, Dict, Set +from typing import Optional, List, Callable from mypy_extensions import trait from mypy.nodes import ( From ae0043f673429dc564528af08e8773fea66d58c2 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 18 Jan 2019 15:27:19 +0000 Subject: [PATCH 39/47] Clarify code --- mypy/semanal.py | 16 +++++++++++++--- test-data/unit/check-final.test | 9 +++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index b2ed59367599..bb2ef45a5531 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2134,11 +2134,13 @@ def analyze_name_lvalue(self, # Since the is_new_def flag is set, this must have been analyzed # already in the first pass and added to the symbol table. # An exception is typing module with incomplete test fixtures. + assert lval.node.name() in self.globals or self.cur_mod_id == 'typing' + # A previously defined name cannot be redefined as a final name even when + # using renaming. if (is_final - and unmangle(lval.name) + "'" in self.globals - and unmangle(lval.name) + "'" != lval.name): + and self.is_mangled_global(lval.name) + and not self.is_initial_mangled_global(lval.name)): self.fail("Cannot redefine an existing name as final", lval) - assert lval.node.name() in self.globals or self.cur_mod_id == 'typing' elif (self.locals[-1] is not None and lval.name not in self.locals[-1] and lval.name not in self.global_decls[-1] and lval.name not in self.nonlocal_decls[-1]): @@ -2159,6 +2161,14 @@ def analyze_name_lvalue(self, else: self.make_name_lvalue_point_to_existing_def(lval, explicit_type, is_final) + def is_mangled_global(self, name: str) -> bool: + # A global is mangled if there exists at least one renamed variant. + return unmangle(name) + "'" in self.globals + + def is_initial_mangled_global(self, name: str) -> bool: + # If there are renamed definitions for a global, the first one has exactly one prime. + return name == unmangle(name) + "'" + def is_alias_for_final_name(self, name: str) -> bool: if self.is_func_scope(): if not name.endswith("'"): diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index 369df31760eb..6e5b68c69f87 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -364,6 +364,15 @@ z: Final = 2 # E: Name 'z' already defined on line 23 \ z = 3 # E: Cannot assign to final name "z" [case testFinalReassignModuleVar2] +# flags: --allow-redefinition +from typing import Final +y = 1 +y +y = 2 +y +y: Final = 3 # E: Cannot redefine an existing name as final + +[case testFinalReassignModuleVar3] # flags: --disallow-redefinition from typing import Final From 512c1edbae62eb6875990e5f66ab6c7ee2eec0e8 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 18 Jan 2019 15:39:34 +0000 Subject: [PATCH 40/47] Test additional special case --- test-data/unit/check-final.test | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index 6e5b68c69f87..be17b1da1333 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -366,6 +366,14 @@ z = 3 # E: Cannot assign to final name "z" [case testFinalReassignModuleVar2] # flags: --allow-redefinition from typing import Final + +x: Final = 1 +x +def f() -> int: + global x + x = 3 # E: Cannot assign to final name "x" + return x + y = 1 y y = 2 From 1f7e36d60e10652f4e3e33398cc501e8800ce168 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 18 Jan 2019 15:42:44 +0000 Subject: [PATCH 41/47] Add test case --- test-data/unit/check-redefine.test | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index 9f64dbfc5324..e1e38f9d96b4 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -449,3 +449,15 @@ def g() -> None: _ = '' x, _ = 0, C() [builtins fixtures/tuple.pyi] + +[case testRedefineAsException] +# flags: --allow-redefinition +e = 1 +reveal_type(e) # E: Revealed type is 'builtins.int' +try: + pass +except Exception as e: + reveal_type(e) # E: Revealed type is 'builtins.Exception' +e = '' +reveal_type(e) # E: Revealed type is 'builtins.str' +[builtins fixtures/exception.pyi] From 72043ae645ca0f2be426a4b9d0d518e6acd2db1a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 18 Jan 2019 16:00:31 +0000 Subject: [PATCH 42/47] Fix with statements --- mypy/renaming.py | 7 ++++++- test-data/unit/check-redefine.test | 13 +++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/mypy/renaming.py b/mypy/renaming.py index 74d2b74d90db..6100d6a2a948 100644 --- a/mypy/renaming.py +++ b/mypy/renaming.py @@ -132,8 +132,13 @@ def visit_try_stmt(self, stmt: TryStmt) -> None: self.leave_with_or_try() def visit_with_stmt(self, stmt: WithStmt) -> None: + for expr in stmt.expr: + expr.accept(self) + for target in stmt.target: + if target is not None: + self.analyze_lvalue(target) self.enter_with_or_try() - super().visit_with_stmt(stmt) + stmt.body.accept(self) self.leave_with_or_try() def visit_import(self, imp: Import) -> None: diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index e1e38f9d96b4..eef95ccf9154 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -461,3 +461,16 @@ except Exception as e: e = '' reveal_type(e) # E: Revealed type is 'builtins.str' [builtins fixtures/exception.pyi] + +[case testRedefineUsingWithStatement] +# flags: --allow-redefinition +class A: + def __enter__(self) -> int: ... + def __exit__(self, x, y, z) -> None: ... +class B: + def __enter__(self) -> str: ... + def __exit__(self, x, y, z) -> None: ... +with A() as x: + reveal_type(x) # E: Revealed type is 'builtins.int' +with B() as x: + x = 0 # E: Incompatible types in assignment (expression has type "int", variable has type "str") From 8c30c90a45b7e069cf005d2f507c0b680c46888c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 18 Jan 2019 16:22:49 +0000 Subject: [PATCH 43/47] Remove unnecessary check --- mypy/renaming.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mypy/renaming.py b/mypy/renaming.py index 6100d6a2a948..1bf33de58537 100644 --- a/mypy/renaming.py +++ b/mypy/renaming.py @@ -192,9 +192,8 @@ def visit_name_expr(self, expr: NameExpr) -> None: def handle_arg(self, name: str) -> None: """Store function argument.""" - if name not in self.refs[-1]: - self.refs[-1][name] = [[]] - self.num_reads[-1][name] = 0 + self.refs[-1][name] = [[]] + self.num_reads[-1][name] = 0 def handle_def(self, expr: NameExpr) -> None: """Store new name definition.""" From e390f3872e9e62f2e98d371b79c9d77beb60638e Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 18 Jan 2019 16:35:07 +0000 Subject: [PATCH 44/47] Add comment --- mypy/renaming.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mypy/renaming.py b/mypy/renaming.py index 1bf33de58537..b3c26466b6d7 100644 --- a/mypy/renaming.py +++ b/mypy/renaming.py @@ -127,6 +127,9 @@ def visit_continue_stmt(self, stmt: ContinueStmt) -> None: self.reject_redefinition_of_vars_in_loop() def visit_try_stmt(self, stmt: TryStmt) -> None: + # Variables defined by a try statement get special treatment in the + # type checker which allows them to be always redefined, so no need to + # do renaming here. self.enter_with_or_try() super().visit_try_stmt(stmt) self.leave_with_or_try() From bc0ee196c5ecd4d53593f2c9099a92be72fbcb8e Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 18 Jan 2019 16:50:39 +0000 Subject: [PATCH 45/47] Ensure renaming cannot happen across scopes in class --- mypy/renaming.py | 8 +++++++- test-data/unit/semanal-statements.test | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/mypy/renaming.py b/mypy/renaming.py index b3c26466b6d7..429d28934c60 100644 --- a/mypy/renaming.py +++ b/mypy/renaming.py @@ -3,7 +3,7 @@ from mypy.nodes import ( Block, AssignmentStmt, NameExpr, MypyFile, FuncDef, Lvalue, ListExpr, TupleExpr, TempNode, WhileStmt, ForStmt, BreakStmt, ContinueStmt, TryStmt, WithStmt, StarExpr, ImportFrom, - MemberExpr, IndexExpr, Import + MemberExpr, IndexExpr, Import, ClassDef ) from mypy.traverser import TraverserVisitor @@ -99,6 +99,12 @@ def visit_func_def(self, fdef: FuncDef) -> None: self.leave_block() self.leave_scope() + def visit_class_def(self, cdef: ClassDef) -> None: + self.reject_redefinition_of_vars_in_scope() + self.enter_scope() + super().visit_class_def(cdef) + self.leave_scope() + def visit_block(self, block: Block) -> None: self.enter_block() super().visit_block(block) diff --git a/test-data/unit/semanal-statements.test b/test-data/unit/semanal-statements.test index 682c1dfa0b89..b6136da37f6b 100644 --- a/test-data/unit/semanal-statements.test +++ b/test-data/unit/semanal-statements.test @@ -1033,3 +1033,26 @@ MypyFile:1( NameExpr(f [__main__.f]) Args( NameExpr(a' [l]))))))) + +[case testCannotRenameExternalVarWithinClass] +# flags: --allow-redefinition +x = 0 +x +class A: + x = 1 +x = '' +[out] +MypyFile:1( + AssignmentStmt:2( + NameExpr(x* [__main__.x]) + IntExpr(0)) + ExpressionStmt:3( + NameExpr(x [__main__.x])) + ClassDef:4( + A + AssignmentStmt:5( + NameExpr(x* [m]) + IntExpr(1))) + AssignmentStmt:6( + NameExpr(x [__main__.x]) + StrExpr())) From 54b86d8c2374d123f5ad4d024d2519729542b4ff Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 18 Jan 2019 16:51:48 +0000 Subject: [PATCH 46/47] Remove unnecessary call --- mypy/renaming.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mypy/renaming.py b/mypy/renaming.py index 429d28934c60..a04caa1f3e4e 100644 --- a/mypy/renaming.py +++ b/mypy/renaming.py @@ -79,8 +79,6 @@ def visit_func_def(self, fdef: FuncDef) -> None: # Conservatively do not allow variable defined before a function to # be redefined later, since function could refer to either definition. self.reject_redefinition_of_vars_in_scope() - # Functions can't be redefined. - self.record_assignment(fdef.name(), can_be_redefined=False) self.enter_scope() self.enter_block() From 496d2ddb1bc7a21c1bfb299e0e7f241d80368e9c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 18 Jan 2019 17:00:30 +0000 Subject: [PATCH 47/47] The final assignments takes precedence in class when renaming --- mypy/renaming.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/mypy/renaming.py b/mypy/renaming.py index a04caa1f3e4e..d348599e4166 100644 --- a/mypy/renaming.py +++ b/mypy/renaming.py @@ -7,6 +7,16 @@ ) from mypy.traverser import TraverserVisitor +MYPY = False +if MYPY: + from typing_extensions import Final + + +# Scope kinds +FILE = 0 # type: Final +FUNCTION = 1 # type: Final +CLASS = 2 # type: Final + class VariableRenameVisitor(TraverserVisitor): """Rename variables to allow redefinition of variables. @@ -59,6 +69,8 @@ def __init__(self) -> None: self.refs = [] # type: List[Dict[str, List[List[NameExpr]]]] # Number of reads of the most recent definition of a variable (per scope) self.num_reads = [] # type: List[Dict[str, int]] + # Kinds of nested scopes (FILE, FUNCTION or CLASS) + self.scope_kinds = [] # type: List[int] def visit_mypy_file(self, file_node: MypyFile) -> None: """Rename variables within a file. @@ -66,7 +78,7 @@ def visit_mypy_file(self, file_node: MypyFile) -> None: This is the main entry point to this class. """ self.clear() - self.enter_scope() + self.enter_scope(FILE) self.enter_block() for d in file_node.defs: @@ -80,7 +92,7 @@ def visit_func_def(self, fdef: FuncDef) -> None: # be redefined later, since function could refer to either definition. self.reject_redefinition_of_vars_in_scope() - self.enter_scope() + self.enter_scope(FUNCTION) self.enter_block() for arg in fdef.arguments: @@ -99,7 +111,7 @@ def visit_func_def(self, fdef: FuncDef) -> None: def visit_class_def(self, cdef: ClassDef) -> None: self.reject_redefinition_of_vars_in_scope() - self.enter_scope() + self.enter_scope(CLASS) super().visit_class_def(cdef) self.leave_scope() @@ -234,7 +246,7 @@ def flush_refs(self) -> None: This will be called at the end of a scope. """ - is_func = self.is_nested() + is_func = self.scope_kinds[-1] == FUNCTION for name, refs in self.refs[-1].items(): if len(refs) == 1: # Only one definition -- no renaming neeed. @@ -286,15 +298,17 @@ def leave_loop(self) -> None: def current_block(self) -> int: return self.blocks[-1] - def enter_scope(self) -> None: + def enter_scope(self, kind: int) -> None: self.var_blocks.append({}) self.refs.append({}) self.num_reads.append({}) + self.scope_kinds.append(kind) def leave_scope(self) -> None: self.flush_refs() self.var_blocks.pop() self.num_reads.pop() + self.scope_kinds.pop() def is_nested(self) -> int: return len(self.var_blocks) > 1