From 4e6ecb2348941fe757511941959f85ab0886416f Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Fri, 12 Jul 2024 05:53:37 -0700 Subject: [PATCH] Treat `not` operations as boolean tests (#12301) ## Summary Closes https://github.com/astral-sh/ruff/issues/12285. --- .../resources/test/fixtures/ruff/RUF019.py | 4 ++ crates/ruff_linter/src/checkers/ast/mod.rs | 7 ++++ ..._rules__ruff__tests__RUF019_RUF019.py.snap | 42 ++++++++++++++++++- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF019.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF019.py index ffcdb032e2..89e22e9d97 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF019.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF019.py @@ -13,6 +13,10 @@ if (k) in d and d[k]: if k in d and d[(k)]: pass +not ("key" in dct and dct["key"]) + +bool("key" in dct and dct["key"]) + # OK v = "k" in d and d["k"] diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index 88d42fddee..42713917bc 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -1140,6 +1140,13 @@ impl<'a> Visitor<'a> for Checker<'a> { self.visit_expr(body); self.visit_expr(orelse); } + Expr::UnaryOp(ast::ExprUnaryOp { + op: UnaryOp::Not, + operand, + range: _, + }) => { + self.visit_boolean_test(operand); + } Expr::Call(ast::ExprCall { func, arguments, diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF019_RUF019.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF019_RUF019.py.snap index c8ab5a8af9..f536eb250a 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF019_RUF019.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF019_RUF019.py.snap @@ -77,6 +77,46 @@ RUF019.py:13:4: RUF019 [*] Unnecessary key check before dictionary access 13 |+if d.get((k)): 14 14 | pass 15 15 | -16 16 | # OK +16 16 | not ("key" in dct and dct["key"]) +RUF019.py:16:6: RUF019 [*] Unnecessary key check before dictionary access + | +14 | pass +15 | +16 | not ("key" in dct and dct["key"]) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF019 +17 | +18 | bool("key" in dct and dct["key"]) + | + = help: Replace with `dict.get` +ℹ Safe fix +13 13 | if k in d and d[(k)]: +14 14 | pass +15 15 | +16 |-not ("key" in dct and dct["key"]) + 16 |+not (dct.get("key")) +17 17 | +18 18 | bool("key" in dct and dct["key"]) +19 19 | + +RUF019.py:18:6: RUF019 [*] Unnecessary key check before dictionary access + | +16 | not ("key" in dct and dct["key"]) +17 | +18 | bool("key" in dct and dct["key"]) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF019 +19 | +20 | # OK + | + = help: Replace with `dict.get` + +ℹ Safe fix +15 15 | +16 16 | not ("key" in dct and dct["key"]) +17 17 | +18 |-bool("key" in dct and dct["key"]) + 18 |+bool(dct.get("key")) +19 19 | +20 20 | # OK +21 21 | v = "k" in d and d["k"]