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.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

B018 doesn't trigger for useless expressions involving multiple variables

swierh opened this issue · comments

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

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

"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:

  • UnaryOp, like +1, not x, ~3. I don't know of any code patterns that would trigger false positives.
  • BinOp, like 1 + 1 or 2 << 3. This is what the OP is asking for. This could have false positives with Airflow code, where you do dag << task to add a task to a dag.
  • Compare, like 1 < 2. Can't think of any problems with this one.
  • BoolOp, like a and b. People could be using this for conditional execution.
  • GeneratorExp, like (x for x in y). That is definitely useless.
  • JoinedStr, which is an f-string. Having an f-string in a line by itself doesn't have any use.

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

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().