diff --git a/mypy/newsemanal/semanal.py b/mypy/newsemanal/semanal.py index 2248be743d50..f557e604173f 100644 --- a/mypy/newsemanal/semanal.py +++ b/mypy/newsemanal/semanal.py @@ -2563,6 +2563,11 @@ def analyze_member_lvalue(self, lval: MemberExpr, explicit_type: bool, is_final: if cur_node and is_final: # Overrides will be checked in type checker. self.fail("Cannot redefine an existing name as final", lval) + # On first encounter with this definition, if this attribute was defined before + # with an inferred type and it's marked with an explicit type now, give an error. + if (not lval.node and cur_node and isinstance(cur_node.node, Var) and + cur_node.node.is_inferred and explicit_type): + self.attribute_already_defined(lval.name, lval, cur_node) # 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. @@ -4298,9 +4303,9 @@ def name_not_defined(self, name: str, ctx: Context, namespace: Optional[str] = N # Yes. Generate a helpful note. self.add_fixture_note(fullname, ctx) - def name_already_defined(self, name: str, ctx: Context, - original_ctx: Optional[Union[SymbolTableNode, SymbolNode]] = None - ) -> None: + def already_defined(self, name: str, ctx: Context, + original_ctx: Optional[Union[SymbolTableNode, SymbolNode]] = None, *, + noun: str) -> None: if isinstance(original_ctx, SymbolTableNode): node = original_ctx.node # type: Optional[SymbolNode] elif isinstance(original_ctx, SymbolNode): @@ -4319,7 +4324,17 @@ 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(unmangle(name), extra_msg), ctx) + self.fail("{} '{}' already defined{}".format(noun, unmangle(name), extra_msg), ctx) + + def name_already_defined(self, name: str, ctx: Context, + original_ctx: Optional[Union[SymbolTableNode, SymbolNode]] = None + ) -> None: + self.already_defined(name, ctx, original_ctx, noun='Name') + + def attribute_already_defined(self, name: str, ctx: Context, + original_ctx: Optional[Union[SymbolTableNode, SymbolNode]] = None + ) -> None: + self.already_defined(name, ctx, original_ctx, noun='Attribute') def is_local_name(self, name: str) -> bool: """Does name look like reference to a definition in the current module?""" diff --git a/mypy/semanal.py b/mypy/semanal.py index 04ea3d8d3914..72a7c9320972 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2288,6 +2288,11 @@ def analyze_member_lvalue(self, lval: MemberExpr, explicit_type: bool, is_final: if cur_node and is_final: # Overrides will be checked in type checker. self.fail("Cannot redefine an existing name as final", lval) + # If this attribute was defined before with an inferred type and it's marked + # with an explicit type now, give an error. + if (cur_node and isinstance(cur_node.node, Var) and cur_node.node.is_inferred and + explicit_type): + self.attribute_already_defined(lval.name, lval, cur_node) # 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. @@ -3736,8 +3741,9 @@ def name_not_defined(self, name: str, ctx: Context) -> None: # Yes. Generate a helpful note. self.add_fixture_note(fullname, ctx) - def name_already_defined(self, name: str, ctx: Context, - original_ctx: Optional[Union[SymbolTableNode, SymbolNode]] = None) -> None: + def already_defined(self, name: str, ctx: Context, + original_ctx: Optional[Union[SymbolTableNode, SymbolNode]] = None, *, + noun: str) -> None: if isinstance(original_ctx, SymbolTableNode): node = original_ctx.node elif isinstance(original_ctx, SymbolNode): @@ -3752,7 +3758,17 @@ 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(unmangle(name), extra_msg), ctx) + self.fail("{} '{}' already defined{}".format(noun, unmangle(name), extra_msg), ctx) + + def name_already_defined(self, name: str, ctx: Context, + original_ctx: Optional[Union[SymbolTableNode, SymbolNode]] = None + ) -> None: + self.already_defined(name, ctx, original_ctx, noun='Name') + + def attribute_already_defined(self, name: str, ctx: Context, + original_ctx: Optional[Union[SymbolTableNode, SymbolNode]] = None + ) -> None: + self.already_defined(name, ctx, original_ctx, noun='Attribute') def fail(self, msg: str, ctx: Context, serious: bool = False, *, blocker: bool = False) -> None: diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index b8e9cda74ee3..f40373c30211 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -5838,6 +5838,18 @@ def test() -> None: reveal_type(x) # E: Revealed type is 'Union[Type[__main__.One], Type[__main__.Other]]' [builtins fixtures/isinstancelist.pyi] +[case testMemberRedefinition] +class C: + def __init__(self) -> None: + self.foo = 12 + self.foo: int = 12 # E: Attribute 'foo' already defined on line 3 + +[case testMemberRedefinitionDefinedInClass] +class C: + foo = 12 + def __init__(self) -> None: + self.foo: int = 12 # E: Attribute 'foo' already defined on line 2 + [case testAbstractInit] from abc import abstractmethod, ABCMeta class A(metaclass=ABCMeta):