Skip to content
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

unnecessary-list-index-lookup doesn't detect list mutation in a nested while loop #6818

Closed
skirpichev opened this issue Jun 3, 2022 · 6 comments · Fixed by #6845
Closed
Assignees
Labels
Blocker 🙅 Blocks the next release False Positive 🦟 A message is emitted but nothing is wrong with the code
Milestone

Comments

@skirpichev
Copy link
Contributor

Bug description

Simple example:

$ cat -n a.py 
     1  eqs = [1, 2, 3, 11, 4]
     2  for i, e in enumerate(eqs):
     3      while eqs[i] < 10:
     4          eqs[i] += 1

Similar issue has unnecessary-dict-index-lookup

BTW, another simple failure is #6788. These checkers seems to be too naive to be enabled per default (as in the 2.14).

Configuration

None

Command used

pylint -j1 a.py

Pylint output

************* Module a
a.py:1:0: C0114: Missing module docstring (missing-module-docstring)
a.py:3:10: R1736: Unnecessary list index lookup, use 'e' instead (unnecessary-list-index-lookup)

------------------------------------------------------------------
Your code has been rated at 5.00/10 (previous run: 5.00/10, +0.00)

Expected behavior

No R1736.

Pylint version

pylint 2.14.0
astroid 2.11.5
Python 3.10.2+ (heads/3.10:75c5dbc27e, Feb 19 2022, 06:58:32) [GCC 10.2.1 20210110]

OS / Environment

No response

Additional dependencies

No response

@skirpichev skirpichev added the Needs triage 📥 Just created, needs acknowledgment, triage, and proper labelling label Jun 3, 2022
@DanielNoord DanielNoord added False Positive 🦟 A message is emitted but nothing is wrong with the code and removed Needs triage 📥 Just created, needs acknowledgment, triage, and proper labelling labels Jun 3, 2022
@DanielNoord
Copy link
Collaborator

/CC @timmartin

We have a test for:

for idx, val in enumerate(my_list):
    my_list[idx] = 42

Seems like we also need to exclude other forms of assignment which we are likely not doing as of yet. Perhaps by checking for mixins.AssignTypeMixin instead of nodes.Assign.

I'm putting this in 2.14.1 as a fix seem quite easy and necessary in order to not to raise too many false positives.

@DanielNoord DanielNoord added this to the 2.14.1 milestone Jun 3, 2022
@timmartin
Copy link
Contributor

This doesn't seem all that simple to me. I don't think the problem is just a matter of Assign vs. AugAssign. The problem seems to be that the code marked with the comment

# Ignore this subscript if it is the target of an assignment
# Early termination; after reassignment index lookup will be necessary

makes the assumption that if it proceeds through the tree in order and terminates early when it is assigned, then we won't warn on any reference that can happen after the assignment. For / While loop conditions are cases where this assumption is violated, since the condition will be evaluated again after the body of the loop.

The simple solution would just be to suppress the warning for the whole loop any time there's a write to the subscript anywhere wihin the body. This will cause some false negatives, but it seems not unreasonable to me; if the for loop is updating the enumerated data, then maybe it's clearer just to do the index lookup every time and be explicit about it.

If we want to analyze this properly then I can write some code that detects the for / while loop conditional and then recursively checks inside the body for assignments. However, this seems like we're getting into control flow analysis, which I gather is something that we're trying to solve more generally in Pylint. I'm not sure about writing a whole bunch of ad hoc code for this one case.

skirpichev added a commit to skirpichev/diofant that referenced this issue Jun 4, 2022
@DanielNoord
Copy link
Collaborator

This doesn't seem all that simple to me. I don't think the problem is just a matter of Assign vs. AugAssign. The problem seems to be that the code marked with the comment

# Ignore this subscript if it is the target of an assignment
# Early termination; after reassignment index lookup will be necessary

makes the assumption that if it proceeds through the tree in order and terminates early when it is assigned, then we won't warn on any reference that can happen after the assignment. For / While loop conditions are cases where this assumption is violated, since the condition will be evaluated again after the body of the loop.

The simple solution would just be to suppress the warning for the whole loop any time there's a write to the subscript anywhere wihin the body. This will cause some false negatives, but it seems not unreasonable to me; if the for loop is updating the enumerated data, then maybe it's clearer just to do the index lookup every time and be explicit about it.

If we want to analyze this properly then I can write some code that detects the for / while loop conditional and then recursively checks inside the body for assignments. However, this seems like we're getting into control flow analysis, which I gather is something that we're trying to solve more generally in Pylint. I'm not sure about writing a whole bunch of ad hoc code for this one case.

Yeah, let's create a PR now that might cause some false negatives and worry about fixing those later. Better to have false negatives than to have false positives. Would you be willing to prepare that PR?

@timmartin
Copy link
Contributor

Yes, I can create a PR this weekend.

@DanielNoord DanielNoord added the Blocker 🙅 Blocks the next release label Jun 4, 2022
skirpichev added a commit to skirpichev/diofant that referenced this issue Jun 4, 2022
@Pierre-Sassoulas
Copy link
Member

@DanielNoord is this really a blocker for 2.14.1 ? We have two crashes in the already fixed issues and we can release 2.14.2 with only this as soon as it's merged if required.

@DanielNoord
Copy link
Collaborator

Yeah, I think this is quite a big flaw in the newly added checker. It's not likely that people will update their dependencies in the weekends and its Pentecost. So let's wait to see if we can get a fix merged this weekend. If we can't then let's release without one.

timmartin added a commit to timmartin/pylint that referenced this issue Jun 5, 2022
The ``unnecessary-list-index-lookup`` checker was assuming that if the
subscript was written to then it would only invalidate access on later
lines, but this is not necessarily the case if an inner loop is nested
inside the one being checked.

Fixes pylint-dev#6818
@Pierre-Sassoulas Pierre-Sassoulas modified the milestones: 2.14.1, 2.14.2 Jun 6, 2022
Pierre-Sassoulas pushed a commit that referenced this issue Jun 11, 2022
…6845)

The ``unnecessary-list-index-lookup`` checker was assuming that if the
subscript was written to then it would only invalidate access on later
lines, but this is not necessarily the case if an inner loop is nested
inside the one being checked.

Fixes #6818

Co-authored-by: Jacob Walls <jacobtylerwalls@gmail.com>
Pierre-Sassoulas pushed a commit to Pierre-Sassoulas/pylint that referenced this issue Jun 15, 2022
…ylint-dev#6845)

The ``unnecessary-list-index-lookup`` checker was assuming that if the
subscript was written to then it would only invalidate access on later
lines, but this is not necessarily the case if an inner loop is nested
inside the one being checked.

Fixes pylint-dev#6818

Co-authored-by: Jacob Walls <jacobtylerwalls@gmail.com>
Pierre-Sassoulas pushed a commit that referenced this issue Jun 15, 2022
…6845)

The ``unnecessary-list-index-lookup`` checker was assuming that if the
subscript was written to then it would only invalidate access on later
lines, but this is not necessarily the case if an inner loop is nested
inside the one being checked.

Fixes #6818

Co-authored-by: Jacob Walls <jacobtylerwalls@gmail.com>
skirpichev added a commit to skirpichev/diofant that referenced this issue Jun 16, 2022
skirpichev added a commit to skirpichev/diofant that referenced this issue Jun 17, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Blocker 🙅 Blocks the next release False Positive 🦟 A message is emitted but nothing is wrong with the code
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants