Skip to content

Commit ebd0479

Browse files
onlinedilevkivskyi
authored andcommitted
Error when a class member is redefined (#6686)
Fixes #6571.
1 parent e8a72a0 commit ebd0479

File tree

3 files changed

+50
-7
lines changed

3 files changed

+50
-7
lines changed

mypy/newsemanal/semanal.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2563,6 +2563,11 @@ def analyze_member_lvalue(self, lval: MemberExpr, explicit_type: bool, is_final:
25632563
if cur_node and is_final:
25642564
# Overrides will be checked in type checker.
25652565
self.fail("Cannot redefine an existing name as final", lval)
2566+
# On first encounter with this definition, if this attribute was defined before
2567+
# with an inferred type and it's marked with an explicit type now, give an error.
2568+
if (not lval.node and cur_node and isinstance(cur_node.node, Var) and
2569+
cur_node.node.is_inferred and explicit_type):
2570+
self.attribute_already_defined(lval.name, lval, cur_node)
25662571
# If the attribute of self is not defined in superclasses, create a new Var, ...
25672572
if ((node is None or isinstance(node.node, Var) and node.node.is_abstract_var) or
25682573
# ... 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
42984303
# Yes. Generate a helpful note.
42994304
self.add_fixture_note(fullname, ctx)
43004305

4301-
def name_already_defined(self, name: str, ctx: Context,
4302-
original_ctx: Optional[Union[SymbolTableNode, SymbolNode]] = None
4303-
) -> None:
4306+
def already_defined(self, name: str, ctx: Context,
4307+
original_ctx: Optional[Union[SymbolTableNode, SymbolNode]] = None, *,
4308+
noun: str) -> None:
43044309
if isinstance(original_ctx, SymbolTableNode):
43054310
node = original_ctx.node # type: Optional[SymbolNode]
43064311
elif isinstance(original_ctx, SymbolNode):
@@ -4319,7 +4324,17 @@ def name_already_defined(self, name: str, ctx: Context,
43194324
extra_msg = ' on line {}'.format(node.line)
43204325
else:
43214326
extra_msg = ' (possibly by an import)'
4322-
self.fail("Name '{}' already defined{}".format(unmangle(name), extra_msg), ctx)
4327+
self.fail("{} '{}' already defined{}".format(noun, unmangle(name), extra_msg), ctx)
4328+
4329+
def name_already_defined(self, name: str, ctx: Context,
4330+
original_ctx: Optional[Union[SymbolTableNode, SymbolNode]] = None
4331+
) -> None:
4332+
self.already_defined(name, ctx, original_ctx, noun='Name')
4333+
4334+
def attribute_already_defined(self, name: str, ctx: Context,
4335+
original_ctx: Optional[Union[SymbolTableNode, SymbolNode]] = None
4336+
) -> None:
4337+
self.already_defined(name, ctx, original_ctx, noun='Attribute')
43234338

43244339
def is_local_name(self, name: str) -> bool:
43254340
"""Does name look like reference to a definition in the current module?"""

mypy/semanal.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2288,6 +2288,11 @@ def analyze_member_lvalue(self, lval: MemberExpr, explicit_type: bool, is_final:
22882288
if cur_node and is_final:
22892289
# Overrides will be checked in type checker.
22902290
self.fail("Cannot redefine an existing name as final", lval)
2291+
# If this attribute was defined before with an inferred type and it's marked
2292+
# with an explicit type now, give an error.
2293+
if (cur_node and isinstance(cur_node.node, Var) and cur_node.node.is_inferred and
2294+
explicit_type):
2295+
self.attribute_already_defined(lval.name, lval, cur_node)
22912296
# If the attribute of self is not defined in superclasses, create a new Var, ...
22922297
if ((node is None or isinstance(node.node, Var) and node.node.is_abstract_var) or
22932298
# ... 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:
37363741
# Yes. Generate a helpful note.
37373742
self.add_fixture_note(fullname, ctx)
37383743

3739-
def name_already_defined(self, name: str, ctx: Context,
3740-
original_ctx: Optional[Union[SymbolTableNode, SymbolNode]] = None) -> None:
3744+
def already_defined(self, name: str, ctx: Context,
3745+
original_ctx: Optional[Union[SymbolTableNode, SymbolNode]] = None, *,
3746+
noun: str) -> None:
37413747
if isinstance(original_ctx, SymbolTableNode):
37423748
node = original_ctx.node
37433749
elif isinstance(original_ctx, SymbolNode):
@@ -3752,7 +3758,17 @@ def name_already_defined(self, name: str, ctx: Context,
37523758
extra_msg = ' on line {}'.format(node.line)
37533759
else:
37543760
extra_msg = ' (possibly by an import)'
3755-
self.fail("Name '{}' already defined{}".format(unmangle(name), extra_msg), ctx)
3761+
self.fail("{} '{}' already defined{}".format(noun, unmangle(name), extra_msg), ctx)
3762+
3763+
def name_already_defined(self, name: str, ctx: Context,
3764+
original_ctx: Optional[Union[SymbolTableNode, SymbolNode]] = None
3765+
) -> None:
3766+
self.already_defined(name, ctx, original_ctx, noun='Name')
3767+
3768+
def attribute_already_defined(self, name: str, ctx: Context,
3769+
original_ctx: Optional[Union[SymbolTableNode, SymbolNode]] = None
3770+
) -> None:
3771+
self.already_defined(name, ctx, original_ctx, noun='Attribute')
37563772

37573773
def fail(self, msg: str, ctx: Context, serious: bool = False, *,
37583774
blocker: bool = False) -> None:

test-data/unit/check-classes.test

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5838,6 +5838,18 @@ def test() -> None:
58385838
reveal_type(x) # E: Revealed type is 'Union[Type[__main__.One], Type[__main__.Other]]'
58395839
[builtins fixtures/isinstancelist.pyi]
58405840

5841+
[case testMemberRedefinition]
5842+
class C:
5843+
def __init__(self) -> None:
5844+
self.foo = 12
5845+
self.foo: int = 12 # E: Attribute 'foo' already defined on line 3
5846+
5847+
[case testMemberRedefinitionDefinedInClass]
5848+
class C:
5849+
foo = 12
5850+
def __init__(self) -> None:
5851+
self.foo: int = 12 # E: Attribute 'foo' already defined on line 2
5852+
58415853
[case testAbstractInit]
58425854
from abc import abstractmethod, ABCMeta
58435855
class A(metaclass=ABCMeta):

0 commit comments

Comments
 (0)