diff --git a/README.md b/README.md index 1d89f11230..463bedb212 100644 --- a/README.md +++ b/README.md @@ -467,6 +467,7 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com | B011 | DoNotAssertFalse | Do not `assert False` (`python -O` removes these calls), raise `AssertionError()` | 🛠 | | B013 | RedundantTupleInExceptionHandler | A length-one tuple literal is redundant. Write `except ValueError:` instead of `except (ValueError,):`. | | | B014 | DuplicateHandlerException | Exception handler with duplicate exception: `ValueError` | 🛠 | +| B015 | UselessComparison | Pointless comparison. This comparison does nothing but waste CPU instructions. Either prepend `assert` or remove it. | | | B017 | NoAssertRaisesException | `assertRaises(Exception):` should be considered evil. | | | B018 | UselessExpression | Found useless expression. Either assign it to a variable or remove it. | | | B025 | DuplicateTryBlockException | try-except block with duplicate exception `Exception` | | diff --git a/resources/test/fixtures/B015.py b/resources/test/fixtures/B015.py new file mode 100644 index 0000000000..cc7c08f01b --- /dev/null +++ b/resources/test/fixtures/B015.py @@ -0,0 +1,24 @@ +assert 1 == 1 + +1 == 1 + +assert 1 in (1, 2) + +1 in (1, 2) + + +if 1 == 2: + pass + + +def test(): + assert 1 in (1, 2) + + 1 in (1, 2) + + +data = [x for x in [1, 2, 3] if x in (1, 2)] + + +class TestClass: + 1 == 1 diff --git a/src/check_ast.rs b/src/check_ast.rs index 522051189b..e1c32935a0 100644 --- a/src/check_ast.rs +++ b/src/check_ast.rs @@ -1326,6 +1326,12 @@ where self.locate_check(Range::from_located(expr)), )); } + + if self.settings.enabled.contains(&CheckCode::B015) { + if let Some(parent) = self.parents.last() { + flake8_bugbear::plugins::useless_comparison(self, left, parent); + } + } } ExprKind::Constant { value: Constant::Str(value), diff --git a/src/checks.rs b/src/checks.rs index 2797eb08c5..95461a0f70 100644 --- a/src/checks.rs +++ b/src/checks.rs @@ -83,6 +83,7 @@ pub enum CheckCode { B011, B013, B014, + B015, B017, B018, B025, @@ -298,6 +299,7 @@ pub enum CheckKind { DoNotAssertFalse, RedundantTupleInExceptionHandler(String), DuplicateHandlerException(Vec), + UselessComparison, NoAssertRaisesException, UselessExpression, DuplicateTryBlockException(String), @@ -484,6 +486,7 @@ impl CheckCode { CheckKind::RedundantTupleInExceptionHandler("ValueError".to_string()) } CheckCode::B014 => CheckKind::DuplicateHandlerException(vec!["ValueError".to_string()]), + CheckCode::B015 => CheckKind::UselessComparison, CheckCode::B017 => CheckKind::NoAssertRaisesException, CheckCode::B018 => CheckKind::UselessExpression, CheckCode::B025 => CheckKind::DuplicateTryBlockException("Exception".to_string()), @@ -676,6 +679,7 @@ impl CheckCode { CheckCode::B011 => CheckCategory::Flake8Bugbear, CheckCode::B013 => CheckCategory::Flake8Bugbear, CheckCode::B014 => CheckCategory::Flake8Bugbear, + CheckCode::B015 => CheckCategory::Flake8Bugbear, CheckCode::B017 => CheckCategory::Flake8Bugbear, CheckCode::B018 => CheckCategory::Flake8Bugbear, CheckCode::B025 => CheckCategory::Flake8Bugbear, @@ -835,6 +839,7 @@ impl CheckKind { CheckKind::DoNotAssertFalse => &CheckCode::B011, CheckKind::RedundantTupleInExceptionHandler(_) => &CheckCode::B013, CheckKind::DuplicateHandlerException(_) => &CheckCode::B014, + CheckKind::UselessComparison => &CheckCode::B015, CheckKind::NoAssertRaisesException => &CheckCode::B017, CheckKind::UselessExpression => &CheckCode::B018, CheckKind::DuplicateTryBlockException(_) => &CheckCode::B025, @@ -1111,6 +1116,10 @@ impl CheckKind { `except ({name},):`." ) } + CheckKind::UselessComparison => "Pointless comparison. This comparison does nothing \ + but waste CPU instructions. Either prepend `assert` \ + or remove it." + .to_string(), CheckKind::DuplicateHandlerException(names) => { if names.len() == 1 { let name = &names[0]; diff --git a/src/checks_gen.rs b/src/checks_gen.rs index a63567b653..2dd173164c 100644 --- a/src/checks_gen.rs +++ b/src/checks_gen.rs @@ -23,6 +23,7 @@ pub enum CheckCodePrefix { B011, B013, B014, + B015, B017, B018, B02, @@ -258,6 +259,7 @@ impl CheckCodePrefix { CheckCode::B011, CheckCode::B013, CheckCode::B014, + CheckCode::B015, CheckCode::B017, CheckCode::B018, CheckCode::B025, @@ -277,16 +279,20 @@ impl CheckCodePrefix { CheckCodePrefix::B002 => vec![CheckCode::B002], CheckCodePrefix::B006 => vec![CheckCode::B006], CheckCodePrefix::B007 => vec![CheckCode::B007], - CheckCodePrefix::B01 => vec![ - CheckCode::B011, - CheckCode::B013, - CheckCode::B014, - CheckCode::B017, - CheckCode::B018, - ], + CheckCodePrefix::B01 => { + vec![ + CheckCode::B011, + CheckCode::B013, + CheckCode::B014, + CheckCode::B015, + CheckCode::B017, + CheckCode::B018, + ] + } CheckCodePrefix::B011 => vec![CheckCode::B011], CheckCodePrefix::B013 => vec![CheckCode::B013], CheckCodePrefix::B014 => vec![CheckCode::B014], + CheckCodePrefix::B015 => vec![CheckCode::B015], CheckCodePrefix::B017 => vec![CheckCode::B017], CheckCodePrefix::B018 => vec![CheckCode::B018], CheckCodePrefix::B02 => vec![CheckCode::B025], @@ -910,6 +916,7 @@ impl CheckCodePrefix { CheckCodePrefix::B011 => PrefixSpecificity::Explicit, CheckCodePrefix::B013 => PrefixSpecificity::Explicit, CheckCodePrefix::B014 => PrefixSpecificity::Explicit, + CheckCodePrefix::B015 => PrefixSpecificity::Explicit, CheckCodePrefix::B017 => PrefixSpecificity::Explicit, CheckCodePrefix::B018 => PrefixSpecificity::Explicit, CheckCodePrefix::B02 => PrefixSpecificity::Tens, diff --git a/src/flake8_bugbear/plugins/mod.rs b/src/flake8_bugbear/plugins/mod.rs index e58ad901bf..3ec5ea8a83 100644 --- a/src/flake8_bugbear/plugins/mod.rs +++ b/src/flake8_bugbear/plugins/mod.rs @@ -5,6 +5,7 @@ pub use mutable_argument_default::mutable_argument_default; pub use redundant_tuple_in_exception_handler::redundant_tuple_in_exception_handler; pub use unary_prefix_increment::unary_prefix_increment; pub use unused_loop_control_variable::unused_loop_control_variable; +pub use useless_comparison::useless_comparison; pub use useless_expression::useless_expression; mod assert_false; @@ -14,4 +15,5 @@ mod mutable_argument_default; mod redundant_tuple_in_exception_handler; mod unary_prefix_increment; mod unused_loop_control_variable; +mod useless_comparison; mod useless_expression; diff --git a/src/flake8_bugbear/plugins/useless_comparison.rs b/src/flake8_bugbear/plugins/useless_comparison.rs new file mode 100644 index 0000000000..529d36de7f --- /dev/null +++ b/src/flake8_bugbear/plugins/useless_comparison.rs @@ -0,0 +1,14 @@ +use rustpython_ast::{Expr, Stmt, StmtKind}; + +use crate::ast::types::{CheckLocator, Range}; +use crate::check_ast::Checker; +use crate::checks::{Check, CheckKind}; + +pub fn useless_comparison(checker: &mut Checker, expr: &Expr, parent: &Stmt) { + if let StmtKind::Expr { .. } = &parent.node { + checker.add_check(Check::new( + CheckKind::UselessComparison, + checker.locate_check(Range::from_located(expr)), + )); + } +} diff --git a/src/linter.rs b/src/linter.rs index 5cfaf08efb..7321e5a55c 100644 --- a/src/linter.rs +++ b/src/linter.rs @@ -298,6 +298,7 @@ mod tests { #[test_case(CheckCode::B011, Path::new("B011.py"); "B011")] #[test_case(CheckCode::B013, Path::new("B013.py"); "B013")] #[test_case(CheckCode::B014, Path::new("B014.py"); "B014")] + #[test_case(CheckCode::B015, Path::new("B015.py"); "B015")] #[test_case(CheckCode::B017, Path::new("B017.py"); "B017")] #[test_case(CheckCode::B018, Path::new("B018.py"); "B018")] #[test_case(CheckCode::B025, Path::new("B025.py"); "B025")] diff --git a/src/snapshots/ruff__linter__tests__B015_B015.py.snap b/src/snapshots/ruff__linter__tests__B015_B015.py.snap new file mode 100644 index 0000000000..d68d846045 --- /dev/null +++ b/src/snapshots/ruff__linter__tests__B015_B015.py.snap @@ -0,0 +1,37 @@ +--- +source: src/linter.rs +expression: checks +--- +- kind: UselessComparison + location: + row: 3 + column: 0 + end_location: + row: 3 + column: 1 + fix: ~ +- kind: UselessComparison + location: + row: 7 + column: 0 + end_location: + row: 7 + column: 1 + fix: ~ +- kind: UselessComparison + location: + row: 17 + column: 4 + end_location: + row: 17 + column: 5 + fix: ~ +- kind: UselessComparison + location: + row: 24 + column: 4 + end_location: + row: 24 + column: 5 + fix: ~ +