diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF060.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF060.py index 52ea18de39..c3b96c6b7b 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF060.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF060.py @@ -19,6 +19,9 @@ b'c' in b"" b"a" in bytearray() b"a" in bytes() 1 in frozenset() +1 in set(set()) +2 in frozenset([]) +'' in set("") # OK 1 in [2] @@ -35,3 +38,7 @@ b'c' in b"x" b"a" in bytearray([2]) b"a" in bytes("a", "utf-8") 1 in frozenset("c") +1 in set(set((1,2))) +1 in set(set([1])) +'' in {""} +frozenset() in {frozenset()} diff --git a/crates/ruff_linter/src/rules/ruff/rules/in_empty_collection.rs b/crates/ruff_linter/src/rules/ruff/rules/in_empty_collection.rs index 142b968b1b..9132508e8c 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/in_empty_collection.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/in_empty_collection.rs @@ -1,6 +1,7 @@ use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, ViolationMetadata}; use ruff_python_ast::{self as ast, CmpOp, Expr}; +use ruff_python_semantic::SemanticModel; use ruff_text_size::Ranged; use crate::checkers::ast::Checker; @@ -48,6 +49,14 @@ pub(crate) fn in_empty_collection(checker: &Checker, compare: &ast::ExprCompare) }; let semantic = checker.semantic(); + + if is_empty(right, semantic) { + checker.report_diagnostic(Diagnostic::new(InEmptyCollection, compare.range())); + } +} + +fn is_empty(expr: &Expr, semantic: &SemanticModel) -> bool { + let set_methods = ["set", "frozenset"]; let collection_methods = [ "list", "tuple", @@ -59,7 +68,7 @@ pub(crate) fn in_empty_collection(checker: &Checker, compare: &ast::ExprCompare) "str", ]; - let is_empty_collection = match right { + match expr { Expr::List(ast::ExprList { elts, .. }) => elts.is_empty(), Expr::Tuple(ast::ExprTuple { elts, .. }) => elts.is_empty(), Expr::Set(ast::ExprSet { elts, .. }) => elts.is_empty(), @@ -75,15 +84,19 @@ pub(crate) fn in_empty_collection(checker: &Checker, compare: &ast::ExprCompare) arguments, range: _, }) => { - arguments.is_empty() - && collection_methods + if arguments.is_empty() { + collection_methods .iter() .any(|s| semantic.match_builtin_expr(func, s)) + } else if let Some(arg) = arguments.find_positional(0) { + set_methods + .iter() + .any(|s| semantic.match_builtin_expr(func, s)) + && is_empty(arg, semantic) + } else { + false + } } _ => false, - }; - - if is_empty_collection { - checker.report_diagnostic(Diagnostic::new(InEmptyCollection, compare.range())); } } diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF060_RUF060.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF060_RUF060.py.snap index a269459cd7..ac92e967a9 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF060_RUF060.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF060_RUF060.py.snap @@ -187,6 +187,7 @@ RUF060.py:20:1: RUF060 Unnecessary membership test on empty collection 20 | b"a" in bytes() | ^^^^^^^^^^^^^^^ RUF060 21 | 1 in frozenset() +22 | 1 in set(set()) | RUF060.py:21:1: RUF060 Unnecessary membership test on empty collection @@ -195,6 +196,35 @@ RUF060.py:21:1: RUF060 Unnecessary membership test on empty collection 20 | b"a" in bytes() 21 | 1 in frozenset() | ^^^^^^^^^^^^^^^^ RUF060 -22 | -23 | # OK +22 | 1 in set(set()) +23 | 2 in frozenset([]) + | + +RUF060.py:22:1: RUF060 Unnecessary membership test on empty collection + | +20 | b"a" in bytes() +21 | 1 in frozenset() +22 | 1 in set(set()) + | ^^^^^^^^^^^^^^^ RUF060 +23 | 2 in frozenset([]) +24 | '' in set("") + | + +RUF060.py:23:1: RUF060 Unnecessary membership test on empty collection + | +21 | 1 in frozenset() +22 | 1 in set(set()) +23 | 2 in frozenset([]) + | ^^^^^^^^^^^^^^^^^^ RUF060 +24 | '' in set("") + | + +RUF060.py:24:1: RUF060 Unnecessary membership test on empty collection + | +22 | 1 in set(set()) +23 | 2 in frozenset([]) +24 | '' in set("") + | ^^^^^^^^^^^^^ RUF060 +25 | +26 | # OK |