Repository: python/mypy

Issue:
No error when type annotation for `self.foo` comes after type is already inferred
(Not sure if this is a dupe.)

This is not an error:
```
class C:
    def __init__(self) -> None:
        self.foo = 12
        self.foo: int = 12  # Expected: self.foo is already defined on line 4
```
Even though this is:
```
foo = 12
foo: int = 12   # E: Name 'foo' already defined on line 1
```
It's the same if the annotation has the form of a `# type:` comment.

It's particularly obnoxious if the inferred type is wider than the type annotation, e.g. `Any` -- the inferred type silently wins.


Discussion:
Note this also happens with the new semantic analyzer.
I would like to work on this issue, but I am unable to reproduce the bug. Any help on how I may be able to reproduce this?
I can reproduce this false negative. Just copy it to a file and type-check it. Also I don't think it is a good issue for beginners.

Resolving patch:
diff --git a/mypy/newsemanal/semanal.py b/mypy/newsemanal/semanal.py
--- 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
--- 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:


Test patch:
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
--- 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):