Ticket #2718 (closed enhancement: fixed)

Opened 2 years ago

Last modified 1 year ago

Warn about unused variables in methods

Reported by: jml Assigned to: exarkun
Priority: normal Milestone:
Component: Pyflakes Severity: normal
Keywords: Cc:
Estimated Completion (YYYY/MM/DD): Branch: branches/unused-variable-flakes-2718-2
Author: jml

Description

It would be nice if pyflakes warned about unused variables in methods. Not sure what else to say.

Attachments

unused-in-function-2718.diff (4.6 kB) - added by jml on 10/04/08 08:44:00.
unused-in-function-2718-2.diff (10.8 kB) - added by jml on 11/04/08 10:35:02.
Updated patch, replying to exarkun's comments.
unused-in-function-2718-2-progress.diff (5.4 kB) - added by jml on 11/05/08 05:25:05.
Diff between 2 and 3 -- style changes.
unused-in-function-2718-3.diff (13.1 kB) - added by jml on 11/05/08 05:25:55.
New diff with style changes.
unused-in-function-2718-4.diff (14.0 kB) - added by jml on 06/03/09 05:59:13.
Latest version of patch.
unused-in-function-2718-3-progress.diff (2.3 kB) - added by jml on 06/03/09 06:06:50.
Incremental diff from patch 3

Change History

09/06/08 08:12:17 changed by exarkun

Are you talking about something like this?

def foo(bar):
    baz = bar + 2
    return 12

I guess it would indeed be useful if this generated a warning about how baz is unused,

10/04/08 07:30:11 changed by jml

Yes, that's exactly what I mean.

10/04/08 08:43:42 changed by jml

I've uploaded a branch to Launchpad that addresses this bug: https://code.edge.launchpad.net/~jml/pyflakes/unused-in-function-2718. I'll attach a diff momentarily.

In the diff I add a new message UnusedVariable and add some code near the end of runFunction that loops over variables in scope and warns if they weren't used. To avoid spurious warnings, I check that the binding is an instance of Assignment. To make that actually work, I added a new Binding type called Argument and made the argument bindings use that and I changed class definition just to use a raw Binding rather than an assignment.

The tests are in test_other, this seemed as good a place as any. After I got the new feature working I had to update a bunch of the existing tests to actually use their locally defined variables.

I cleaned up vertical whitespace occasionally (although now that I think about it, I went PEP 8 rather than Divmod — oops).

This patch is more of an RFC than an actual request to merge. If you (whoever you are) are happy with the approach then please do a thorough review. If not, tell me a better approach and I'll resubmit.

Thanks, jml

10/04/08 08:44:00 changed by jml

  • attachment unused-in-function-2718.diff added.

10/04/08 09:12:30 changed by jml

  • keywords set to review.
  • author set to jml.

10/07/08 20:09:40 changed by jml

  • owner deleted.

Assigning to no-one, following glyph's advice.

10/14/08 15:27:14 changed by exarkun

  • keywords deleted.
  • owner set to jml.

Hmm. I like the direction of the patch, but now that I can try the feature against existing code, I see some unexpected side-effects.

  • unused names in for loops and list comprehensions: it's quite common to have names bound by a loop which then aren't used; it's particularly common when doing tuple unpacking, as often not all of the fields are of interest. The current patch warns about a lot of these in the code I looked at, and the warnings are basically just noise. It might be worth warning if there is tuple unpacking and none of the names are used (or maybe that would end up just being noise too).
  • names bound in a loop: a warning is emitted for a name assigned later in any loop construct if the name isn't used lexically afterwards, but the loop body may be executed again and the result of the assignment used by a lexically earlier piece of code, so these warnings are just wrong most of the time

Also, names which are closed over are reported as unused if only the nested scope reads them. Hopefully that's just a simple bug, nothing complicated.

Sorry for the delay on the review, thanks for working on this. Even after filtering out all the false positives, I found a bunch of dead code while doing this review.

11/04/08 10:35:02 changed by jml

  • attachment unused-in-function-2718-2.diff added.

Updated patch, replying to exarkun's comments.

11/04/08 10:39:34 changed by jml

  • keywords set to review.
  • owner changed from jml to exarkun.

Thanks for the helpful review.

The patch now:

  • explicitly ignores assignments that take place in for loop statements, list comprehensions, generator expressions, tuples and lists. It does this by marking them as "bindings" rather than "assignments". The nomenclature here might need revisiting.
  • Records if the name has already been used in the scope if it is rebound. This fixes the "for loop" problem.
  • Defers unused variable checking until after all functions have been processed. This solves the "closed over" problem you mentioned. The solution here is a little ugly, but tolerable. I think the right thing to do would be to re-jig pyflakes so that it had a more explicit custom event model.
  • Does some VWS normalization.

11/04/08 14:44:36 changed by exarkun

  • keywords deleted.
  • owner changed from exarkun to jml.

Definitely better. There are some coding standard issues, like new classes, methods, parameters, or attributes without documentation and names which use _ as a word separator (in particular, the Binding/Assignment pair could use some good documentation as that's potentially quite confusing). These should be fixed. Otherwise, I'm really liking the patch.

11/05/08 05:25:05 changed by jml

  • attachment unused-in-function-2718-2-progress.diff added.

Diff between 2 and 3 -- style changes.

11/05/08 05:25:55 changed by jml

  • attachment unused-in-function-2718-3.diff added.

New diff with style changes.

11/05/08 05:28:51 changed by jml

  • keywords set to review.
  • owner changed from jml to exarkun.
  • completion_estimate_date changed.

I've attached an updated patch and a diff showing what's changed from the last patch. I'm not sure if I've got all the coding style issues.

11/10/08 16:21:18 changed by exarkun

  • branch set to branches/unused-variable-flakes-2718.
  • branch_author changed from jml to exarkun, jml.

(In [16991]) Branching to 'unused-variable-flakes-2718'

11/10/08 16:30:52 changed by exarkun

(In [16992]) Apply unused-in-function-2718-3.diff

refs #2718

11/10/08 17:11:45 changed by exarkun

  • keywords deleted.
  • owner changed from exarkun to jml.
  • branch_author changed from exarkun, jml to jml.

Cool. A few more pretty simple things:

  1. Checker's two new attributes, _deferredFunctions and _deferredAssignments are undocumented
  2. I didn't realize what was going on with deferAssignment and deferFunction before, but now I think I do. It'd probably be good if the non-reentrancy of each of these functions were documented. It might be even better if they raised an exception if invoked in that way. Alternatively, if they're folded back into a single function which is reentrant, do the users of the API have their requirements satisfied?
  3. deferFunction and deferAssignment say to be called after just before completion. I guess that after isn't supposed to be there?
  4. The parent parameter to the handleNode method is undocumented. Also, does it actually need a default value? The WITH method of Checker is the only caller which doesn't supply a parent. Why doesn't it supply a parent? I guess its parent is always a with node, rather than one of the things that the node's parent is checked against, so the fact that it's missing doesn't matter. I feel kind of bad about the parent being inconsistent in this case, even though it has no end-user visible consequences. It might be surprising to a future developer, or something.

11/10/08 17:34:32 changed by jml

3 & 4 are now fixed.

Re 2 & 1, the reason we have both of these things is that:

def foo:
    def bar:
        def baz:

is processed in the order "foo, bar, baz".

But to handle any assignments that occur in "foo" correctly, we need to have first processed "bar" and "baz".

The "right" thing to do is to ditch the deferred approach and instead have a more event-y system. I don't think this is required for this patch.

I'm not actually sure what you mean by the non-reentrancy of those functions. AIUI, they get re-entered all the time.

11/10/08 17:35:51 changed by jml

  • keywords set to review.
  • owner changed from jml to exarkun.

11/10/08 18:11:22 changed by exarkun

  • keywords deleted.
  • owner changed from exarkun to jml.

Hum. You're right. I failed to understand how that part of the code works. So a large portion of the Pyflakes code executes beneath _runDeferred. That code almost certainly ends up invoking deferFunction, which appends to the list _runDeferred is iterating over. That's okay, though, since iteration eventually gets to those new elements.

I still think there's a potential problem, though. Or at least a hypothetical potential problem. ;) After the deferred functions are processed, the deferred assignments are processed. The code in the deferred assignments can also (but does not, as the code stands) call deferFunction. However, deferred function processing is over and these deferments will not be processed. Perhaps this is the kind of thing which makes you suggest the right solution you did. It'd be good if it failed noisily instead of silently in the mean time, though.

By the way, you didn't attach a new patch to the ticket this time around.\

06/03/09 05:59:13 changed by jml

  • attachment unused-in-function-2718-4.diff added.

Latest version of patch.

06/03/09 06:06:50 changed by jml

  • attachment unused-in-function-2718-3-progress.diff added.

Incremental diff from patch 3

06/03/09 06:09:22 changed by jml

  • keywords set to review.
  • owner changed from jml to exarkun.

So, it's been a long time, but I haven't actually forgotten about this patch. It's been loitering on my todo list with intent.

I've made it so that deferFunction will fail noisily if called after the deferred function processing is over. It's noisy and messy, and I couldn't really think of a nice way to test it. Since this is only a hypothetical potential problem, perhaps we don't need tests or a nice error.

Patches are attached. The unused-in-function-2718-3-progress.diff applies cleanly to the subversion branch, and has the changes I missed in my previous post.

Thanks for the last review, sorry it took so long to get around to it.

06/03/09 11:47:31 changed by exarkun

(In [17422]) Apply unused-in-function-2718-3-progress.diff

refs #2718

06/03/09 11:54:24 changed by exarkun

  • branch changed from branches/unused-variable-flakes-2718 to branches/unused-variable-flakes-2718-2.
  • branch_author changed from jml to exarkun, jml.

(In [17423]) Branching to 'unused-variable-flakes-2718-2'

06/03/09 11:55:00 changed by exarkun

  • branch_author changed from exarkun, jml to jml.

06/03/09 12:21:55 changed by exarkun

(In [17425]) Fix some docstrings/test method names

refs #2718

06/03/09 14:53:07 changed by exarkun

  • keywords deleted.

With a few simple (mostly doc) changes, I think this looks good. I'll go ahead and merge.

06/03/09 14:59:32 changed by exarkun

  • status changed from new to closed.
  • resolution set to fixed.

(In [17427]) Merge unused-variable-flakes-2718-2

Author: jml Reviewer: exarkun Fixes: #2718

Add support for detecting local variables which are bound but then never used.

jethro@divmod.org