-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Error when a class member is redefined #6686
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Error when a class member is redefined #6686
Conversation
Note that your fix broke a whole bunch of new analyzer tests. |
Fixed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Here I have some comments.
@@ -2563,6 +2563,9 @@ 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 (not lval.node and cur_node and isinstance(cur_node.node, Var) and | |||
cur_node.node.is_inferred and explicit_type): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH I don't follow the logic here, maybe add a comment explaining this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I can write a comment. But before, let me explain: Fail if passing first time, the node exists and it's a Var
node, it's type is inferred, and new assignment has an explicit type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically yes. But please try to avoid this:
x += 1 # Increment x
Describe the intent and how the code achieves it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@onlined Are you going to add this comment (also in the old analyzer)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I will.
test-data/unit/check-classes.test
Outdated
class C: | ||
def __init__(self) -> None: | ||
self.foo = 12 | ||
self.foo: int = 12 # E: Name 'self.foo' already defined on line 3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please also add a test for something like this:
class C:
foo = 12
def __init__(self) -> None:
self.foo: int = 12 # E: Attribute 'foo' already defined on line 2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You didn't add this test.
mypy/semanal.py
Outdated
@@ -2288,6 +2288,9 @@ 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 (cur_node and isinstance(cur_node.node, Var) and cur_node.node.is_inferred and | |||
explicit_type): | |||
self.name_already_defined('self.' + lval.name, lval, cur_node) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't use this error message because self.foo
is not a name, it is an attribute. I would better use Attribute "foo" already defined ...
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
mypy/newsemanal/semanal.py
Outdated
@@ -2563,6 +2563,9 @@ 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 (not lval.node and cur_node and isinstance(cur_node.node, Var) and | |||
cur_node.node.is_inferred and explicit_type): | |||
self.name_already_defined('self.' + lval.name, lval, cur_node) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using this in the new analyzer is dangerous, it not not only shows the error, but also has some special code for deferring unknown names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I couldn't understand what you meant. Could you explain it a little more?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually confused with with name_not_defined()
. This one is OK. Anyway, did you check that in the situation where an error is emitted, no new variable created, and the l.h.s. points to an existing one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, basically if cur_node
is not None
, l.h.s. points to an existing variable.
test-data/unit/check-newsemanal.test
Outdated
def __init__(self) -> None: | ||
self.foo = 12 | ||
self.foo: int = 12 # E: Name 'self.foo' already defined on line 3 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not very sure about this. Should I really add same tests to new semantic analyzer tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to duplicate tests, most test are currently run with both analyzers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed redundant tests.
Thanks for updates! I left few more comments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for updates! I have just one remaining question, otherwise this PR is ready.
@@ -2563,6 +2563,9 @@ 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 (not lval.node and cur_node and isinstance(cur_node.node, Var) and | |||
cur_node.node.is_inferred and explicit_type): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@onlined Are you going to add this comment (also in the old analyzer)?
Fixes #6571.