diff --git a/mypy/checker.py b/mypy/checker.py index b758af4ad5e6..4db6e905c256 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -94,7 +94,6 @@ 'DeferredNode', [ ('node', DeferredNodeType), - ('context_type_name', Optional[str]), # Name of the surrounding class (for error messages) ('active_typeinfo', Optional[TypeInfo]), # And its TypeInfo (for semantic analysis # self type handling) ]) @@ -105,7 +104,6 @@ 'FineGrainedDeferredNode', [ ('node', FineGrainedDeferredNodeType), - ('context_type_name', Optional[str]), ('active_typeinfo', Optional[TypeInfo]), ]) @@ -329,7 +327,7 @@ def check_second_pass(self, assert not self.deferred_nodes self.deferred_nodes = [] done = set() # type: Set[Union[DeferredNodeType, FineGrainedDeferredNodeType]] - for node, type_name, active_typeinfo in todo: + for node, active_typeinfo in todo: if node in done: continue # This is useful for debugging: @@ -371,14 +369,10 @@ def defer_node(self, node: DeferredNodeType, enclosing_class: Optional[TypeInfo] enclosing_class: for methods, the class where the method is defined NOTE: this can't handle nested functions/methods. """ - if self.errors.type_name: - type_name = self.errors.type_name[-1] - else: - type_name = None # We don't freeze the entire scope since only top-level functions and methods # can be deferred. Only module/class level scope information is needed. # Module-level scope information is preserved in the TypeChecker instance. - self.deferred_nodes.append(DeferredNode(node, type_name, enclosing_class)) + self.deferred_nodes.append(DeferredNode(node, enclosing_class)) def handle_cannot_determine_type(self, name: str, context: Context) -> None: node = self.scope.top_non_lambda_function() diff --git a/mypy/errors.py b/mypy/errors.py index 77cf3fabbc42..136611ccd747 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -172,7 +172,6 @@ def initialize(self) -> None: self.error_info_map = OrderedDict() self.flushed_files = set() self.import_ctx = [] - self.type_name = [None] self.function_or_member = [None] self.ignored_lines = OrderedDict() self.used_ignored_lines = defaultdict(set) @@ -192,7 +191,6 @@ def copy(self) -> 'Errors': self.read_source) new.file = self.file new.import_ctx = self.import_ctx[:] - new.type_name = self.type_name[:] new.function_or_member = self.function_or_member[:] new.target_module = self.target_module new.scope = self.scope diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 27501bc92271..4de55fc46c59 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -580,7 +580,7 @@ def do_func_def(self, n: Union[ast3.FunctionDef, ast3.AsyncFunctionDef], # Before 3.8, [typed_]ast the line number points to the first decorator. # In 3.8, it points to the 'def' line, where we want it. lineno += len(n.decorator_list) - end_lineno = None + end_lineno = None # type: Optional[int] else: # Set end_lineno to the old pre-3.8 lineno, in order to keep # existing "# type: ignore" comments working: diff --git a/mypy/fixup.py b/mypy/fixup.py index 1f82873bd2dd..0a89bae0757c 100644 --- a/mypy/fixup.py +++ b/mypy/fixup.py @@ -83,7 +83,7 @@ def visit_symbol_table(self, symtab: SymbolTable) -> None: assert stnode.node is not None value.node = stnode.node elif not self.allow_missing: - assert stnode is not None, "Could not find cross-ref %s" % (cross_ref,) + assert False, "Could not find cross-ref %s" % (cross_ref,) else: # We have a missing crossref in allow missing mode, need to put something value.node = missing_info(self.modules) diff --git a/mypy/server/update.py b/mypy/server/update.py index 1e35af4f8247..b0a8fc2092b6 100644 --- a/mypy/server/update.py +++ b/mypy/server/update.py @@ -1028,11 +1028,9 @@ def not_found() -> None: node = modules[module] # type: Optional[SymbolNode] file = None # type: Optional[MypyFile] active_class = None - active_class_name = None for c in components: if isinstance(node, TypeInfo): active_class = node - active_class_name = node.name() if isinstance(node, MypyFile): file = node if (not isinstance(node, (MypyFile, TypeInfo)) @@ -1057,7 +1055,7 @@ def not_found() -> None: # a deserialized TypeInfo with missing attributes. not_found() return [], None - result = [FineGrainedDeferredNode(file, None, None)] + result = [FineGrainedDeferredNode(file, None)] stale_info = None # type: Optional[TypeInfo] if node.is_protocol: stale_info = node @@ -1082,7 +1080,7 @@ def not_found() -> None: # context will be wrong and it could be a partially initialized deserialized node. not_found() return [], None - return [FineGrainedDeferredNode(node, active_class_name, active_class)], None + return [FineGrainedDeferredNode(node, active_class)], None def is_verbose(manager: BuildManager) -> bool: diff --git a/mypyc/genops.py b/mypyc/genops.py index 8e3185cf4649..09528b77ee69 100644 --- a/mypyc/genops.py +++ b/mypyc/genops.py @@ -2123,7 +2123,7 @@ def visit_operator_assignment_stmt(self, stmt: OperatorAssignmentStmt) -> None: self.assign(target, res, res.line) def get_assignment_target(self, lvalue: Lvalue, - line: Optional[int] = None) -> AssignmentTarget: + line: int = -1) -> AssignmentTarget: if isinstance(lvalue, NameExpr): # If we are visiting a decorator, then the SymbolNode we really want to be looking at # is the function that is decorated, not the entire Decorator node itself. @@ -2168,7 +2168,7 @@ def get_assignment_target(self, lvalue: Lvalue, return AssignmentTargetAttr(obj, lvalue.name) elif isinstance(lvalue, TupleExpr): # Multiple assignment a, ..., b = e - star_idx = None + star_idx = None # type: Optional[int] lvalues = [] for idx, item in enumerate(lvalue.items): targ = self.get_assignment_target(item) @@ -2812,6 +2812,15 @@ def visit_name_expr(self, expr: NameExpr) -> Value: # Except for imports, that currently always happens in the global namespace. if expr.kind == LDEF and not (isinstance(expr.node, Var) and expr.node.is_suppressed_import): + # Try to detect and error when we hit the irritating mypy bug + # where a local variable is cast to None. (#5423) + if (isinstance(expr.node, Var) and is_none_rprimitive(self.node_type(expr)) + and expr.node.is_inferred): + self.error( + "Local variable '{}' has inferred type None; add an annotation".format( + expr.node.name()), + expr.node.line) + # TODO: Behavior currently only defined for Var and FuncDef node types. return self.read(self.get_assignment_target(expr), expr.line) diff --git a/mypyc/test-data/commandline.test b/mypyc/test-data/commandline.test index 80edfe74466f..899b49eb88e4 100644 --- a/mypyc/test-data/commandline.test +++ b/mypyc/test-data/commandline.test @@ -159,3 +159,10 @@ a_str = " ".join(str(i) for i in range(10)) wtvr = next(i for i in range(10) if i == 5) d1 = {1: 2} + +# Make sure we can produce an error when we hit the awful None case +def f(l: List[object]) -> None: + x = None # E: Local variable 'x' has inferred type None; add an annotation + for i in l: + if x is None: + x = i