|
6 | 6 |
|
7 | 7 | import difflib |
8 | 8 | from copy import copy |
| 9 | +from enum import IntFlag, auto |
9 | 10 | from typing import TYPE_CHECKING, TypeGuard, cast |
10 | 11 |
|
11 | 12 | from astroid import nodes |
|
18 | 19 | from pylint.lint import PyLinter |
19 | 20 |
|
20 | 21 |
|
| 22 | +class InvertibleValues(IntFlag): |
| 23 | + NO = 0 |
| 24 | + YES = auto() |
| 25 | + EXPLICIT_NEGATION = auto() |
| 26 | + |
| 27 | + |
21 | 28 | class CodeStyleChecker(BaseChecker): |
22 | 29 | """Checkers that can improve code consistency. |
23 | 30 |
|
@@ -366,16 +373,21 @@ def visit_assign(self, node: nodes.Assign) -> None: |
366 | 373 | ) |
367 | 374 |
|
368 | 375 | @staticmethod |
369 | | - def _can_be_inverted(node: nodes.NodeNG) -> bool: |
370 | | - match node: |
371 | | - case nodes.UnaryOp(op="not"): |
372 | | - return True |
373 | | - case nodes.Compare( |
374 | | - ops=[("!=" | "not in", _)] |
375 | | - | [("<" | "<=" | ">" | ">=", nodes.Const(value=int()))] |
376 | | - ): |
377 | | - return True |
378 | | - return False |
| 376 | + def _can_be_inverted(values: list[nodes.NodeNG]) -> InvertibleValues: |
| 377 | + invertible = InvertibleValues.NO |
| 378 | + for node in values: |
| 379 | + match node: |
| 380 | + case nodes.UnaryOp(op="not") | nodes.Compare( |
| 381 | + ops=[("!=" | "not in", _)] |
| 382 | + ): |
| 383 | + invertible |= InvertibleValues.EXPLICIT_NEGATION |
| 384 | + case nodes.Compare( |
| 385 | + ops=[("<" | "<=" | ">" | ">=", nodes.Const(value=int()))] |
| 386 | + ): |
| 387 | + invertible |= InvertibleValues.YES |
| 388 | + case _: |
| 389 | + return InvertibleValues.NO |
| 390 | + return invertible |
379 | 391 |
|
380 | 392 | @staticmethod |
381 | 393 | def _invert_node(node: nodes.NodeNG) -> nodes.NodeNG: |
@@ -408,7 +420,11 @@ def _invert_node(node: nodes.NodeNG) -> nodes.NodeNG: |
408 | 420 |
|
409 | 421 | @only_required_for_messages("improve-conditionals") |
410 | 422 | def visit_boolop(self, node: nodes.BoolOp) -> None: |
411 | | - if node.op == "or" and all(self._can_be_inverted(val) for val in node.values): |
| 423 | + if ( |
| 424 | + node.op == "or" |
| 425 | + and (invertible := self._can_be_inverted(node.values)) |
| 426 | + and invertible & InvertibleValues.EXPLICIT_NEGATION |
| 427 | + ): |
412 | 428 | new_boolop = copy(node) |
413 | 429 | new_boolop.op = "and" |
414 | 430 | new_boolop.postinit([self._invert_node(val) for val in node.values]) |
|
0 commit comments