PyCQA / flake8-bugbear

A plugin for Flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle.
MIT License
1.05k stars 103 forks source link

B018 doesn't trigger for useless expressions involving multiple variables #452

Open swierh opened 5 months ago

swierh commented 5 months ago

I recently ran into the following bug and was surprised it wasn't flagged by B018:

def some_funcion(a, b, c, d):
    result = a * b
    +c * d

    return result

that should have been

def some_funcion(a, b, c, d):
    result = (
        a * b
        + c * d
    )

    return result

It looks like B018 doesn't trigger for expressions containing multiple variables.

Here's some examples:

a * b     # doesn't trigger B018
+a * b    # doesn't trigger B018
+1 * 2    # doesn't trigger B018
-1 * 2    # doesn't trigger B018
1 * 2 + 3 # doesn't trigger B018
a + 1     # doesn't trigger B018
a         # triggers B018
(a, b)    # triggers B018
1         # triggers B018
+1        # triggers B018
-1        # triggers B018

Versions I'm using:

python version 3.10.13
flake8 version 7.0.0
flake8-bugbear version 23.12.2
ruff version 0.1.13

cooperlees commented 5 months ago

Agree this would be nice to add to B018. Thanks for reporting.

JelleZijlstra commented 5 months ago

"multiple variables" isn't a great description of what's going on here. If you look at the code for B018, it triggers if a statement consists only of an expression that is a set/list/tuple/dict literal or a non-str constant. (The original post claims that B018 triggers for +1 and -1, but I don't think it does.)

We could expand the rule to some other kinds of nodes:

So we could definitely expand B018 to a few more kinds of AST nodes, but there's some room for false positives.

JelleZijlstra commented 5 months ago

I remembered that internally in our codebase we have a similar lint rule. It triggers on these types:

ast.Attribute, ast.Lambda, ast.Dict, ast.Set, ast.Compare, ast.Num, ast.Subscript, ast.List, ast.Tuple, ast.Name

So that's missing a few that I mentioned above. We also flag ast.Call nodes if the called function is one of sorted, reversed, map, and filter. If you write sorted(x) on a line by itself, you probably meant x.sort().