diff --git a/resources/test/fixtures/B018.py b/resources/test/fixtures/B018.py new file mode 100644 index 0000000000..519b3de5ba --- /dev/null +++ b/resources/test/fixtures/B018.py @@ -0,0 +1,55 @@ +class Foo1: + """abc""" + + +class Foo2: + """abc""" + + a = 2 + "str" # Str (no raise) + f"{int}" # JoinedStr (no raise) + 1j # Number (complex) + 1 # Number (int) + 1.0 # Number (float) + b"foo" # Binary + True # NameConstant (True) + False # NameConstant (False) + None # NameConstant (None) + [1, 2] # list + {1, 2} # set + {"foo": "bar"} # dict + + +class Foo3: + 123 + a = 2 + "str" + 1 + + +def foo1(): + """my docstring""" + + +def foo2(): + """my docstring""" + a = 2 + "str" # Str (no raise) + f"{int}" # JoinedStr (no raise) + 1j # Number (complex) + 1 # Number (int) + 1.0 # Number (float) + b"foo" # Binary + True # NameConstant (True) + False # NameConstant (False) + None # NameConstant (None) + [1, 2] # list + {1, 2} # set + {"foo": "bar"} # dict + + +def foo3(): + 123 + a = 2 + "str" + 3 diff --git a/src/check_ast.rs b/src/check_ast.rs index 7730b6c4cd..522051189b 100644 --- a/src/check_ast.rs +++ b/src/check_ast.rs @@ -240,6 +240,7 @@ where decorator_list, returns, args, + body, .. } | StmtKind::AsyncFunctionDef { @@ -247,6 +248,7 @@ where decorator_list, returns, args, + body, .. } => { if self.settings.enabled.contains(&CheckCode::E743) { @@ -300,6 +302,10 @@ where } } + if self.settings.enabled.contains(&CheckCode::B018) { + flake8_bugbear::plugins::useless_expression(self, body); + } + self.check_builtin_shadowing(name, Range::from_located(stmt), true); // Visit the decorators and arguments, but avoid the body, which will be @@ -370,6 +376,7 @@ where bases, keywords, decorator_list, + body, .. } => { if self.settings.enabled.contains(&CheckCode::U004) { @@ -401,6 +408,10 @@ where } } + if self.settings.enabled.contains(&CheckCode::B018) { + flake8_bugbear::plugins::useless_expression(self, body); + } + self.check_builtin_shadowing( name, self.locate_check(Range::from_located(stmt)), diff --git a/src/checks.rs b/src/checks.rs index b6f3845bcf..06f6becde5 100644 --- a/src/checks.rs +++ b/src/checks.rs @@ -84,6 +84,7 @@ pub enum CheckCode { B013, B014, B017, + B018, B025, // flake8-comprehensions C400, @@ -294,6 +295,7 @@ pub enum CheckKind { RedundantTupleInExceptionHandler(String), DuplicateHandlerException(Vec), NoAssertRaisesException, + UselessExpression, DuplicateTryBlockException(String), // flake8-comprehensions UnnecessaryGeneratorList, @@ -476,6 +478,7 @@ impl CheckCode { } CheckCode::B014 => CheckKind::DuplicateHandlerException(vec!["ValueError".to_string()]), CheckCode::B017 => CheckKind::NoAssertRaisesException, + CheckCode::B018 => CheckKind::UselessExpression, CheckCode::B025 => CheckKind::DuplicateTryBlockException("Exception".to_string()), // flake8-comprehensions CheckCode::C400 => CheckKind::UnnecessaryGeneratorList, @@ -665,6 +668,7 @@ impl CheckCode { CheckCode::B013 => CheckCategory::Flake8Bugbear, CheckCode::B014 => CheckCategory::Flake8Bugbear, CheckCode::B017 => CheckCategory::Flake8Bugbear, + CheckCode::B018 => CheckCategory::Flake8Bugbear, CheckCode::B025 => CheckCategory::Flake8Bugbear, CheckCode::C400 => CheckCategory::Flake8Comprehensions, CheckCode::C401 => CheckCategory::Flake8Comprehensions, @@ -822,6 +826,7 @@ impl CheckKind { CheckKind::RedundantTupleInExceptionHandler(_) => &CheckCode::B013, CheckKind::DuplicateHandlerException(_) => &CheckCode::B014, CheckKind::NoAssertRaisesException => &CheckCode::B017, + CheckKind::UselessExpression => &CheckCode::B018, CheckKind::DuplicateTryBlockException(_) => &CheckCode::B025, // flake8-comprehensions CheckKind::UnnecessaryGeneratorList => &CheckCode::C400, @@ -1110,6 +1115,9 @@ impl CheckKind { `assertRaisesRegex`, or use the context manager form of `assertRaises`." .to_string() } + CheckKind::UselessExpression => { + "Found useless expression. Either assign it to a variable or remove it.".to_string() + } CheckKind::DuplicateTryBlockException(name) => { format!("try-except block with duplicate exception `{name}`") } diff --git a/src/checks_gen.rs b/src/checks_gen.rs index a3dbac4d46..77106e4872 100644 --- a/src/checks_gen.rs +++ b/src/checks_gen.rs @@ -26,6 +26,7 @@ pub enum CheckCodePrefix { B013, B014, B017, + B018, B02, B025, C, @@ -256,6 +257,7 @@ impl CheckCodePrefix { CheckCode::B013, CheckCode::B014, CheckCode::B017, + CheckCode::B018, CheckCode::B025, ], CheckCodePrefix::B0 => vec![ @@ -266,6 +268,7 @@ impl CheckCodePrefix { CheckCode::B013, CheckCode::B014, CheckCode::B017, + CheckCode::B018, CheckCode::B025, ], CheckCodePrefix::B00 => vec![CheckCode::B002, CheckCode::B006, CheckCode::B007], @@ -278,12 +281,14 @@ impl CheckCodePrefix { CheckCode::B013, CheckCode::B014, CheckCode::B017, + CheckCode::B018, ] } CheckCodePrefix::B011 => vec![CheckCode::B011], CheckCodePrefix::B013 => vec![CheckCode::B013], CheckCodePrefix::B014 => vec![CheckCode::B014], CheckCodePrefix::B017 => vec![CheckCode::B017], + CheckCodePrefix::B018 => vec![CheckCode::B018], CheckCodePrefix::B02 => vec![CheckCode::B025], CheckCodePrefix::B025 => vec![CheckCode::B025], CheckCodePrefix::C => vec![ @@ -912,6 +917,7 @@ impl CheckCodePrefix { CheckCodePrefix::B013 => PrefixSpecificity::Explicit, CheckCodePrefix::B014 => PrefixSpecificity::Explicit, CheckCodePrefix::B017 => PrefixSpecificity::Explicit, + CheckCodePrefix::B018 => PrefixSpecificity::Explicit, CheckCodePrefix::B02 => PrefixSpecificity::Tens, CheckCodePrefix::B025 => PrefixSpecificity::Explicit, CheckCodePrefix::C => PrefixSpecificity::Category, diff --git a/src/flake8_bugbear/plugins/mod.rs b/src/flake8_bugbear/plugins/mod.rs index 5e905f99ad..e58ad901bf 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_expression::useless_expression; mod assert_false; mod assert_raises_exception; @@ -13,3 +14,4 @@ mod mutable_argument_default; mod redundant_tuple_in_exception_handler; mod unary_prefix_increment; mod unused_loop_control_variable; +mod useless_expression; diff --git a/src/flake8_bugbear/plugins/useless_expression.rs b/src/flake8_bugbear/plugins/useless_expression.rs new file mode 100644 index 0000000000..4867bcda82 --- /dev/null +++ b/src/flake8_bugbear/plugins/useless_expression.rs @@ -0,0 +1,30 @@ +use rustpython_ast::{Constant, ExprKind, Stmt, StmtKind}; + +use crate::ast::types::{CheckLocator, Range}; +use crate::check_ast::Checker; +use crate::checks::{Check, CheckKind}; + +pub fn useless_expression(checker: &mut Checker, body: &[Stmt]) { + for stmt in body { + if let StmtKind::Expr { value } = &stmt.node { + match &value.node { + ExprKind::List { .. } | ExprKind::Dict { .. } | ExprKind::Set { .. } => { + checker.add_check(Check::new( + CheckKind::UselessExpression, + checker.locate_check(Range::from_located(value)), + )); + } + ExprKind::Constant { value: val, .. } => match &val { + Constant::Str { .. } => {} + _ => { + checker.add_check(Check::new( + CheckKind::UselessExpression, + checker.locate_check(Range::from_located(value)), + )); + } + }, + _ => {} + } + } + } +} diff --git a/src/linter.rs b/src/linter.rs index 90cfbffa10..153f81f1c8 100644 --- a/src/linter.rs +++ b/src/linter.rs @@ -262,6 +262,7 @@ mod tests { #[test_case(CheckCode::B013, Path::new("B013.py"); "B013")] #[test_case(CheckCode::B014, Path::new("B014.py"); "B014")] #[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")] #[test_case(CheckCode::C400, Path::new("C400.py"); "C400")] #[test_case(CheckCode::C401, Path::new("C401.py"); "C401")] diff --git a/src/snapshots/ruff__linter__tests__B018_B018.py.snap b/src/snapshots/ruff__linter__tests__B018_B018.py.snap new file mode 100644 index 0000000000..78b0822227 --- /dev/null +++ b/src/snapshots/ruff__linter__tests__B018_B018.py.snap @@ -0,0 +1,197 @@ +--- +source: src/linter.rs +expression: checks +--- +- kind: UselessExpression + location: + row: 11 + column: 4 + end_location: + row: 11 + column: 6 + fix: ~ +- kind: UselessExpression + location: + row: 12 + column: 4 + end_location: + row: 12 + column: 5 + fix: ~ +- kind: UselessExpression + location: + row: 13 + column: 4 + end_location: + row: 13 + column: 7 + fix: ~ +- kind: UselessExpression + location: + row: 14 + column: 4 + end_location: + row: 14 + column: 10 + fix: ~ +- kind: UselessExpression + location: + row: 15 + column: 4 + end_location: + row: 15 + column: 8 + fix: ~ +- kind: UselessExpression + location: + row: 16 + column: 4 + end_location: + row: 16 + column: 9 + fix: ~ +- kind: UselessExpression + location: + row: 17 + column: 4 + end_location: + row: 17 + column: 8 + fix: ~ +- kind: UselessExpression + location: + row: 18 + column: 4 + end_location: + row: 18 + column: 10 + fix: ~ +- kind: UselessExpression + location: + row: 19 + column: 4 + end_location: + row: 19 + column: 10 + fix: ~ +- kind: UselessExpression + location: + row: 20 + column: 4 + end_location: + row: 20 + column: 18 + fix: ~ +- kind: UselessExpression + location: + row: 24 + column: 4 + end_location: + row: 24 + column: 7 + fix: ~ +- kind: UselessExpression + location: + row: 27 + column: 4 + end_location: + row: 27 + column: 5 + fix: ~ +- kind: UselessExpression + location: + row: 39 + column: 4 + end_location: + row: 39 + column: 6 + fix: ~ +- kind: UselessExpression + location: + row: 40 + column: 4 + end_location: + row: 40 + column: 5 + fix: ~ +- kind: UselessExpression + location: + row: 41 + column: 4 + end_location: + row: 41 + column: 7 + fix: ~ +- kind: UselessExpression + location: + row: 42 + column: 4 + end_location: + row: 42 + column: 10 + fix: ~ +- kind: UselessExpression + location: + row: 43 + column: 4 + end_location: + row: 43 + column: 8 + fix: ~ +- kind: UselessExpression + location: + row: 44 + column: 4 + end_location: + row: 44 + column: 9 + fix: ~ +- kind: UselessExpression + location: + row: 45 + column: 4 + end_location: + row: 45 + column: 8 + fix: ~ +- kind: UselessExpression + location: + row: 46 + column: 4 + end_location: + row: 46 + column: 10 + fix: ~ +- kind: UselessExpression + location: + row: 47 + column: 4 + end_location: + row: 47 + column: 10 + fix: ~ +- kind: UselessExpression + location: + row: 48 + column: 4 + end_location: + row: 48 + column: 18 + fix: ~ +- kind: UselessExpression + location: + row: 52 + column: 4 + end_location: + row: 52 + column: 7 + fix: ~ +- kind: UselessExpression + location: + row: 55 + column: 4 + end_location: + row: 55 + column: 5 + fix: ~ +