|
5 | 5 | from __future__ import annotations
|
6 | 6 |
|
7 | 7 | from copy import copy
|
| 8 | +from enum import IntFlag, auto |
8 | 9 | from typing import TYPE_CHECKING, TypeGuard, cast
|
9 | 10 |
|
10 | 11 | from astroid import nodes
|
|
17 | 18 | from pylint.lint import PyLinter
|
18 | 19 |
|
19 | 20 |
|
| 21 | +class InvertibleValues(IntFlag): |
| 22 | + NO = 0 |
| 23 | + YES = auto() |
| 24 | + EXPLICIT_NEGATION = auto() |
| 25 | + |
| 26 | + |
20 | 27 | class CodeStyleChecker(BaseChecker):
|
21 | 28 | """Checkers that can improve code consistency.
|
22 | 29 |
|
@@ -330,16 +337,21 @@ def visit_assign(self, node: nodes.Assign) -> None:
|
330 | 337 | )
|
331 | 338 |
|
332 | 339 | @staticmethod
|
333 |
| - def _can_be_inverted(node: nodes.NodeNG) -> bool: |
334 |
| - match node: |
335 |
| - case nodes.UnaryOp(op="not"): |
336 |
| - return True |
337 |
| - case nodes.Compare( |
338 |
| - ops=[("!=" | "not in", _)] |
339 |
| - | [("<" | "<=" | ">" | ">=", nodes.Const(value=int()))] |
340 |
| - ): |
341 |
| - return True |
342 |
| - return False |
| 340 | + def _can_be_inverted(values: list[nodes.NodeNG]) -> InvertibleValues: |
| 341 | + invertible = InvertibleValues.NO |
| 342 | + for node in values: |
| 343 | + match node: |
| 344 | + case nodes.UnaryOp(op="not") | nodes.Compare( |
| 345 | + ops=[("!=" | "not in", _)] |
| 346 | + ): |
| 347 | + invertible |= InvertibleValues.EXPLICIT_NEGATION |
| 348 | + case nodes.Compare( |
| 349 | + ops=[("<" | "<=" | ">" | ">=", nodes.Const(value=int()))] |
| 350 | + ): |
| 351 | + invertible |= InvertibleValues.YES |
| 352 | + case _: |
| 353 | + return InvertibleValues.NO |
| 354 | + return invertible |
343 | 355 |
|
344 | 356 | @staticmethod
|
345 | 357 | def _invert_node(node: nodes.NodeNG) -> nodes.NodeNG:
|
@@ -372,7 +384,11 @@ def _invert_node(node: nodes.NodeNG) -> nodes.NodeNG:
|
372 | 384 |
|
373 | 385 | @only_required_for_messages("improve-conditionals")
|
374 | 386 | def visit_boolop(self, node: nodes.BoolOp) -> None:
|
375 |
| - if node.op == "or" and all(self._can_be_inverted(val) for val in node.values): |
| 387 | + if ( |
| 388 | + node.op == "or" |
| 389 | + and (invertible := self._can_be_inverted(node.values)) |
| 390 | + and invertible & InvertibleValues.EXPLICIT_NEGATION |
| 391 | + ): |
376 | 392 | new_boolop = copy(node)
|
377 | 393 | new_boolop.op = "and"
|
378 | 394 | new_boolop.postinit([self._invert_node(val) for val in node.values])
|
|
0 commit comments