From 589afd6123f94454e0a763c1b4a3ab02fd202cc7 Mon Sep 17 00:00:00 2001 From: dosisod <39638017+dosisod@users.noreply.github.com> Date: Mon, 18 Mar 2024 23:52:01 -0700 Subject: [PATCH] Detect `x is True or x is False` in FURB191 --- .../checks/readability/use_isinstance_bool.py | 22 ++++++++++++++++--- test/data/err_191.py | 18 +++++++++++++++ test/data/err_191.txt | 2 ++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/refurb/checks/readability/use_isinstance_bool.py b/refurb/checks/readability/use_isinstance_bool.py index da0d59e..c557d8a 100644 --- a/refurb/checks/readability/use_isinstance_bool.py +++ b/refurb/checks/readability/use_isinstance_bool.py @@ -1,8 +1,8 @@ from dataclasses import dataclass -from mypy.nodes import ComparisonExpr, Expression, ListExpr, NameExpr, SetExpr, TupleExpr +from mypy.nodes import ComparisonExpr, Expression, ListExpr, NameExpr, OpExpr, SetExpr, TupleExpr -from refurb.checks.common import stringify +from refurb.checks.common import extract_binary_oper, is_equivalent, stringify from refurb.error import Error @@ -49,7 +49,7 @@ def is_false(expr: Expression) -> bool: return False -def check(node: ComparisonExpr, errors: list[Error]) -> None: +def check(node: ComparisonExpr | OpExpr, errors: list[Error]) -> None: match node: case ComparisonExpr( operators=["in" | "not in" as op], @@ -67,3 +67,19 @@ def check(node: ComparisonExpr, errors: list[Error]) -> None: msg = f"Replace `{old}` with `{new}`" errors.append(ErrorInfo.from_node(node, msg)) + + case OpExpr(): + match extract_binary_oper("or", node): + case ( + ComparisonExpr(operands=[lhs, t], operators=["is"]), + ComparisonExpr(operands=[rhs, f], operators=["is"]), + ) if ( + is_equivalent(lhs, rhs) + and ((is_true(t) and is_false(f)) or (is_false(t) and is_true(f))) + ): + old = stringify(node) + new = f"isinstance({stringify(lhs)}, bool)" + + msg = f"Replace `{old}` with `{new}`" + + errors.append(ErrorInfo.from_node(node, msg)) diff --git a/test/data/err_191.py b/test/data/err_191.py index 6b8953e..c8aa3b2 100644 --- a/test/data/err_191.py +++ b/test/data/err_191.py @@ -27,6 +27,9 @@ if b not in {True, False}: pass +_ = b is True or b is False # noqa: FURB149 +_ = b is False or b is True # noqa: FURB149 + # these should not if b in {True}: pass # noqa: FURB171 @@ -52,3 +55,18 @@ if b == {True, False}: pass if b == {False, True}: pass if b == [True, False]: pass + +b2 = False + +_ = b is True or b is True # noqa: FURB149 +_ = b is False or b is False # noqa: FURB149 +_ = b is True or b is True # noqa: FURB149 +_ = b is True or b is b2 # noqa: FURB149 +_ = b is b2 or b is True # noqa: FURB149 +_ = b is True or b2 is False # noqa: FURB149 +_ = b is not True or b is False # noqa: FURB149 +_ = b is False or b is not True # noqa: FURB149 + +# TODO: support this later +_ = x is not True and x is not False # noqa: FURB149 +_ = x is not False and x is not True # noqa: FURB149 diff --git a/test/data/err_191.txt b/test/data/err_191.txt index c243943..9d30e2b 100644 --- a/test/data/err_191.txt +++ b/test/data/err_191.txt @@ -13,3 +13,5 @@ test/data/err_191.py:20:15 [FURB191]: Replace `b in (True, False)` with `isinsta test/data/err_191.py:23:7 [FURB191]: Replace `b in {True, False}` with `isinstance(b, bool)` test/data/err_191.py:26:8 [FURB191]: Replace `b in {True, False}` with `isinstance(b, bool)` test/data/err_191.py:28:4 [FURB191]: Replace `b not in {True, False}` with `not isinstance(b, bool)` +test/data/err_191.py:30:5 [FURB191]: Replace `b is True or b is False` with `isinstance(b, bool)` +test/data/err_191.py:31:5 [FURB191]: Replace `b is False or b is True` with `isinstance(b, bool)`