diff --git a/README.md b/README.md index 6ddaaef0f1..b1e16609e5 100644 --- a/README.md +++ b/README.md @@ -751,6 +751,7 @@ For more, see [Pylint](https://pypi.org/project/pylint/2.15.7/) on PyPI. | Code | Name | Message | Fix | | ---- | ---- | ------- | --- | +| PLE0206 | 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/property_with_parameters.py b/resources/test/fixtures/pylint/property_with_parameters.py new file mode 100644 index 0000000000..580c319038 --- /dev/null +++ b/resources/test/fixtures/pylint/property_with_parameters.py @@ -0,0 +1,30 @@ +# pylint: disable=missing-docstring, too-few-public-methods +from abc import ABCMeta, abstractmethod + + +class Cls: + @property + def attribute(self, param, param1): # [property-with-parameters] + return param + param1 + + @property + def attribute_keyword_only(self, *, param, param1): # [property-with-parameters] + return param + param1 + + @property + def attribute_positional_only(self, param, param1, /): # [property-with-parameters] + return param + param1 + + +class MyClassBase(metaclass=ABCMeta): + """MyClassBase.""" + + @property + @abstractmethod + def example(self): + """Getter.""" + + @example.setter + @abstractmethod + def example(self, value): + """Setter.""" diff --git a/src/check_ast.rs b/src/check_ast.rs index fe870aea83..a367d73d4a 100644 --- a/src/check_ast.rs +++ b/src/check_ast.rs @@ -441,6 +441,10 @@ where ); } + if self.settings.enabled.contains(&CheckCode::PLE0206) { + pylint::plugins::property_with_parameters(self, stmt, decorator_list, args) + } + self.check_builtin_shadowing(name, Range::from_located(stmt), true); // Visit the decorators and arguments, but avoid the body, which will be diff --git a/src/checks.rs b/src/checks.rs index 3d0e867415..1c3ebb055d 100644 --- a/src/checks.rs +++ b/src/checks.rs @@ -91,6 +91,7 @@ pub enum CheckCode { F841, F901, // pylint errors + PLE0206, PLE1142, // flake8-builtins A001, @@ -540,6 +541,7 @@ pub enum CheckKind { UnusedVariable(String), YieldOutsideFunction(DeferralKeyword), // pylint errors + PropertyWithParameters, AwaitOutsideAsync, // flake8-builtins BuiltinVariableShadowing(String), @@ -825,6 +827,7 @@ impl CheckCode { CheckCode::F841 => CheckKind::UnusedVariable("...".to_string()), CheckCode::F901 => CheckKind::RaiseNotImplemented, // pylint errors + CheckCode::PLE0206 => CheckKind::PropertyWithParameters, CheckCode::PLE1142 => CheckKind::AwaitOutsideAsync, // flake8-builtins CheckCode::A001 => CheckKind::BuiltinVariableShadowing("...".to_string()), @@ -1237,6 +1240,7 @@ impl CheckCode { CheckCode::N817 => CheckCategory::PEP8Naming, CheckCode::N818 => CheckCategory::PEP8Naming, CheckCode::PGH001 => CheckCategory::PygrepHooks, + CheckCode::PLE0206 => CheckCategory::Pylint, CheckCode::PLE1142 => CheckCategory::Pylint, CheckCode::Q000 => CheckCategory::Flake8Quotes, CheckCode::Q001 => CheckCategory::Flake8Quotes, @@ -1350,6 +1354,7 @@ impl CheckKind { CheckKind::NoNewLineAtEndOfFile => &CheckCode::W292, CheckKind::InvalidEscapeSequence(_) => &CheckCode::W605, // pylint errors + CheckKind::PropertyWithParameters => &CheckCode::PLE0206, CheckKind::AwaitOutsideAsync => &CheckCode::PLE1142, // flake8-builtins CheckKind::BuiltinVariableShadowing(_) => &CheckCode::A001, @@ -1723,6 +1728,9 @@ impl CheckKind { format!("Invalid escape sequence: '\\{char}'") } // pylint errors + CheckKind::PropertyWithParameters => { + "Cannot have defined parameters for properties".to_string() + } CheckKind::AwaitOutsideAsync => { "`await` should be used within an async function".to_string() } diff --git a/src/checks_gen.rs b/src/checks_gen.rs index 9768cfee0b..b8c709353c 100644 --- a/src/checks_gen.rs +++ b/src/checks_gen.rs @@ -283,6 +283,10 @@ pub enum CheckCodePrefix { PGH00, PGH001, PLE, + PLE0, + PLE02, + PLE020, + PLE0206, PLE1, PLE11, PLE114, @@ -1156,7 +1160,11 @@ impl CheckCodePrefix { CheckCodePrefix::PGH0 => vec![CheckCode::PGH001], CheckCodePrefix::PGH00 => vec![CheckCode::PGH001], CheckCodePrefix::PGH001 => vec![CheckCode::PGH001], - CheckCodePrefix::PLE => vec![CheckCode::PLE1142], + CheckCodePrefix::PLE => vec![CheckCode::PLE0206, CheckCode::PLE1142], + CheckCodePrefix::PLE0 => vec![CheckCode::PLE0206], + CheckCodePrefix::PLE02 => vec![CheckCode::PLE0206], + CheckCodePrefix::PLE020 => vec![CheckCode::PLE0206], + CheckCodePrefix::PLE0206 => vec![CheckCode::PLE0206], CheckCodePrefix::PLE1 => vec![CheckCode::PLE1142], CheckCodePrefix::PLE11 => vec![CheckCode::PLE1142], CheckCodePrefix::PLE114 => vec![CheckCode::PLE1142], @@ -1628,6 +1636,10 @@ impl CheckCodePrefix { CheckCodePrefix::PGH00 => SuffixLength::Two, CheckCodePrefix::PGH001 => SuffixLength::Three, CheckCodePrefix::PLE => SuffixLength::Zero, + CheckCodePrefix::PLE0 => SuffixLength::One, + CheckCodePrefix::PLE02 => SuffixLength::Two, + CheckCodePrefix::PLE020 => SuffixLength::Three, + CheckCodePrefix::PLE0206 => SuffixLength::Four, CheckCodePrefix::PLE1 => SuffixLength::One, CheckCodePrefix::PLE11 => SuffixLength::Two, CheckCodePrefix::PLE114 => SuffixLength::Three, diff --git a/src/pylint/mod.rs b/src/pylint/mod.rs index 2ddd359159..466da29109 100644 --- a/src/pylint/mod.rs +++ b/src/pylint/mod.rs @@ -11,14 +11,15 @@ mod tests { use crate::linter::test_path; use crate::Settings; - #[test_case(Path::new("await_outside_async.py"))] - fn checks(path: &Path) -> Result<()> { + #[test_case(CheckCode::PLE0206, Path::new("property_with_parameters.py"); "PLE0206")] + #[test_case(CheckCode::PLE1142, Path::new("await_outside_async.py"); "PLE1142")] + fn checks(check_code: CheckCode, path: &Path) -> Result<()> { let snapshot = format!("{}", path.to_string_lossy()); let mut checks = test_path( Path::new("./resources/test/fixtures/pylint") .join(path) .as_path(), - &Settings::for_rules(vec![CheckCode::PLE1142]), + &Settings::for_rules(vec![check_code]), true, )?; checks.sort_by_key(|check| check.location); diff --git a/src/pylint/plugins.rs b/src/pylint/plugins.rs index e9a1908387..bececc1372 100644 --- a/src/pylint/plugins.rs +++ b/src/pylint/plugins.rs @@ -1,10 +1,38 @@ -use rustpython_ast::Expr; +use rustpython_ast::{Arguments, Expr, ExprKind, Stmt}; use crate::ast::types::{FunctionScope, Range, ScopeKind}; use crate::check_ast::Checker; use crate::checks::CheckKind; use crate::Check; +/// PLE0206 +pub fn property_with_parameters( + checker: &mut Checker, + stmt: &Stmt, + decorator_list: &[Expr], + args: &Arguments, +) { + if decorator_list.iter().any(|d| match &d.node { + ExprKind::Name { id, .. } if id == "property" => true, + _ => false, + }) { + if checker.is_builtin("property") + && args + .args + .iter() + .chain(args.posonlyargs.iter()) + .chain(args.kwonlyargs.iter()) + .count() + > 1 + { + checker.add_check(Check::new( + CheckKind::PropertyWithParameters, + Range::from_located(stmt), + )); + } + } +} + /// PLE1142 pub fn await_outside_async(checker: &mut Checker, expr: &Expr) { if !checker diff --git a/src/pylint/snapshots/ruff__pylint__tests__property_with_parameters.py.snap b/src/pylint/snapshots/ruff__pylint__tests__property_with_parameters.py.snap new file mode 100644 index 0000000000..5d7fc52a93 --- /dev/null +++ b/src/pylint/snapshots/ruff__pylint__tests__property_with_parameters.py.snap @@ -0,0 +1,29 @@ +--- +source: src/pylint/mod.rs +expression: checks +--- +- kind: PropertyWithParameters + location: + row: 7 + column: 4 + end_location: + row: 10 + column: 4 + fix: ~ +- kind: PropertyWithParameters + location: + row: 11 + column: 4 + end_location: + row: 14 + column: 4 + fix: ~ +- kind: PropertyWithParameters + location: + row: 15 + column: 4 + end_location: + row: 19 + column: 0 + fix: ~ +