diff --git a/README.md b/README.md index 15144c8455..4d49467d9e 100644 --- a/README.md +++ b/README.md @@ -751,8 +751,9 @@ For more, see [Pylint](https://pypi.org/project/pylint/2.15.7/) on PyPI. | Code | Name | Message | Fix | | ---- | ---- | ------- | --- | -| PLE1142 | AwaitOutsideAsync | `await` should be used within an async function | | +| PLC3002 | UnnecessaryDirectLambdaCall | Lambda expression called directly. Execute the expression inline instead. | | | PLR0206 | PropertyWithParameters | Cannot have defined parameters for properties | | +| PLE1142 | AwaitOutsideAsync | `await` should be used within an async function | | ### Ruff-specific rules diff --git a/resources/test/fixtures/pylint/unnecessary_direct_lambda_call.py b/resources/test/fixtures/pylint/unnecessary_direct_lambda_call.py new file mode 100644 index 0000000000..3894f0004e --- /dev/null +++ b/resources/test/fixtures/pylint/unnecessary_direct_lambda_call.py @@ -0,0 +1,5 @@ +"""Test unnecessary direct calls to lambda expressions.""" +# pylint: disable=undefined-variable, line-too-long + +y = (lambda x: x**2 + 2*x + 1)(a) # [unnecessary-direct-lambda-call] +y = max((lambda x: x**2)(a), (lambda x: x+1)(a)) # [unnecessary-direct-lambda-call,unnecessary-direct-lambda-call] diff --git a/src/check_ast.rs b/src/check_ast.rs index 8dc9f6b229..f32db47f3c 100644 --- a/src/check_ast.rs +++ b/src/check_ast.rs @@ -442,7 +442,7 @@ where } if self.settings.enabled.contains(&CheckCode::PLR0206) { - pylint::plugins::property_with_parameters(self, stmt, decorator_list, args) + pylint::plugins::property_with_parameters(self, stmt, decorator_list, args); } self.check_builtin_shadowing(name, Range::from_located(stmt), true); @@ -1712,6 +1712,11 @@ where pygrep_hooks::checks::no_eval(self, func); } + // pylint + if self.settings.enabled.contains(&CheckCode::PLC3002) { + pylint::plugins::unnecessary_direct_lambda_call(self, expr, func); + } + // Ruff if self.settings.enabled.contains(&CheckCode::RUF101) { rules::plugins::convert_exit_to_sys_exit(self, func); diff --git a/src/checks.rs b/src/checks.rs index ea636ad423..3802c76b71 100644 --- a/src/checks.rs +++ b/src/checks.rs @@ -91,6 +91,7 @@ pub enum CheckCode { F841, F901, // pylint errors + PLC3002, PLR0206, PLE1142, // flake8-builtins @@ -541,6 +542,7 @@ pub enum CheckKind { UnusedVariable(String), YieldOutsideFunction(DeferralKeyword), // pylint errors + UnnecessaryDirectLambdaCall, PropertyWithParameters, AwaitOutsideAsync, // flake8-builtins @@ -827,6 +829,7 @@ impl CheckCode { CheckCode::F841 => CheckKind::UnusedVariable("...".to_string()), CheckCode::F901 => CheckKind::RaiseNotImplemented, // pylint errors + CheckCode::PLC3002 => CheckKind::UnnecessaryDirectLambdaCall, CheckCode::PLR0206 => CheckKind::PropertyWithParameters, CheckCode::PLE1142 => CheckKind::AwaitOutsideAsync, // flake8-builtins @@ -1240,6 +1243,7 @@ impl CheckCode { CheckCode::N817 => CheckCategory::PEP8Naming, CheckCode::N818 => CheckCategory::PEP8Naming, CheckCode::PGH001 => CheckCategory::PygrepHooks, + CheckCode::PLC3002 => CheckCategory::Pylint, CheckCode::PLR0206 => CheckCategory::Pylint, CheckCode::PLE1142 => CheckCategory::Pylint, CheckCode::Q000 => CheckCategory::Flake8Quotes, @@ -1354,6 +1358,7 @@ impl CheckKind { CheckKind::NoNewLineAtEndOfFile => &CheckCode::W292, CheckKind::InvalidEscapeSequence(_) => &CheckCode::W605, // pylint errors + CheckKind::UnnecessaryDirectLambdaCall => &CheckCode::PLC3002, CheckKind::PropertyWithParameters => &CheckCode::PLR0206, CheckKind::AwaitOutsideAsync => &CheckCode::PLE1142, // flake8-builtins @@ -1728,6 +1733,9 @@ impl CheckKind { format!("Invalid escape sequence: '\\{char}'") } // pylint errors + CheckKind::UnnecessaryDirectLambdaCall => "Lambda expression called directly. Execute \ + the expression inline instead." + .to_string(), CheckKind::PropertyWithParameters => { "Cannot have defined parameters for properties".to_string() } diff --git a/src/checks_gen.rs b/src/checks_gen.rs index 004505a040..47939b56d4 100644 --- a/src/checks_gen.rs +++ b/src/checks_gen.rs @@ -282,6 +282,11 @@ pub enum CheckCodePrefix { PGH0, PGH00, PGH001, + PLC, + PLC3, + PLC30, + PLC300, + PLC3002, PLE, PLE1, PLE11, @@ -1161,6 +1166,11 @@ impl CheckCodePrefix { CheckCodePrefix::PGH0 => vec![CheckCode::PGH001], CheckCodePrefix::PGH00 => vec![CheckCode::PGH001], CheckCodePrefix::PGH001 => vec![CheckCode::PGH001], + CheckCodePrefix::PLC => vec![CheckCode::PLC3002], + CheckCodePrefix::PLC3 => vec![CheckCode::PLC3002], + CheckCodePrefix::PLC30 => vec![CheckCode::PLC3002], + CheckCodePrefix::PLC300 => vec![CheckCode::PLC3002], + CheckCodePrefix::PLC3002 => vec![CheckCode::PLC3002], CheckCodePrefix::PLE => vec![CheckCode::PLE1142], CheckCodePrefix::PLE1 => vec![CheckCode::PLE1142], CheckCodePrefix::PLE11 => vec![CheckCode::PLE1142], @@ -1637,6 +1647,11 @@ impl CheckCodePrefix { CheckCodePrefix::PGH0 => SuffixLength::One, CheckCodePrefix::PGH00 => SuffixLength::Two, CheckCodePrefix::PGH001 => SuffixLength::Three, + CheckCodePrefix::PLC => SuffixLength::Zero, + CheckCodePrefix::PLC3 => SuffixLength::One, + CheckCodePrefix::PLC30 => SuffixLength::Two, + CheckCodePrefix::PLC300 => SuffixLength::Three, + CheckCodePrefix::PLC3002 => SuffixLength::Four, CheckCodePrefix::PLE => SuffixLength::Zero, CheckCodePrefix::PLE1 => SuffixLength::One, CheckCodePrefix::PLE11 => SuffixLength::Two, @@ -1741,6 +1756,7 @@ pub const CATEGORIES: &[CheckCodePrefix] = &[ CheckCodePrefix::M, CheckCodePrefix::N, CheckCodePrefix::PGH, + CheckCodePrefix::PLC, CheckCodePrefix::PLE, CheckCodePrefix::PLR, CheckCodePrefix::Q, diff --git a/src/pylint/mod.rs b/src/pylint/mod.rs index 9f2cb1bf03..7ac82ae4c7 100644 --- a/src/pylint/mod.rs +++ b/src/pylint/mod.rs @@ -11,6 +11,7 @@ mod tests { use crate::linter::test_path; use crate::Settings; + #[test_case(CheckCode::PLC3002, Path::new("unnecessary_direct_lambda_call.py"); "PLC3002")] #[test_case(CheckCode::PLR0206, Path::new("property_with_parameters.py"); "PLR0206")] #[test_case(CheckCode::PLE1142, Path::new("await_outside_async.py"); "PLE1142")] fn checks(check_code: CheckCode, path: &Path) -> Result<()> { diff --git a/src/pylint/plugins.rs b/src/pylint/plugins.rs index 6f8834943b..a501b79e77 100644 --- a/src/pylint/plugins.rs +++ b/src/pylint/plugins.rs @@ -5,6 +5,16 @@ use crate::check_ast::Checker; use crate::checks::CheckKind; use crate::Check; +/// PLC3002 +pub fn unnecessary_direct_lambda_call(checker: &mut Checker, expr: &Expr, func: &Expr) { + if let ExprKind::Lambda { .. } = &func.node { + checker.add_check(Check::new( + CheckKind::UnnecessaryDirectLambdaCall, + Range::from_located(expr), + )); + } +} + /// PLR0206 pub fn property_with_parameters( checker: &mut Checker, diff --git a/src/pylint/snapshots/ruff__pylint__tests__unnecessary_direct_lambda_call.py.snap b/src/pylint/snapshots/ruff__pylint__tests__unnecessary_direct_lambda_call.py.snap new file mode 100644 index 0000000000..ca78928511 --- /dev/null +++ b/src/pylint/snapshots/ruff__pylint__tests__unnecessary_direct_lambda_call.py.snap @@ -0,0 +1,29 @@ +--- +source: src/pylint/mod.rs +expression: checks +--- +- kind: UnnecessaryDirectLambdaCall + location: + row: 4 + column: 4 + end_location: + row: 4 + column: 33 + fix: ~ +- kind: UnnecessaryDirectLambdaCall + location: + row: 5 + column: 8 + end_location: + row: 5 + column: 27 + fix: ~ +- kind: UnnecessaryDirectLambdaCall + location: + row: 5 + column: 29 + end_location: + row: 5 + column: 47 + fix: ~ +