diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_return/RET501.py b/crates/ruff_linter/resources/test/fixtures/flake8_return/RET501.py index 9bde6810cd7a1e..57e814d70d6bd4 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_return/RET501.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_return/RET501.py @@ -12,3 +12,8 @@ def get(self, key: str) -> str | None: def get(self, key: str) -> None: print(f"{key} not found") return None + + @property + def prop(self) -> None: + print("Property not found") + return None diff --git a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs index e92fee7e8d45ea..433653c92ecba0 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs @@ -223,7 +223,12 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { Rule::SuperfluousElseContinue, Rule::SuperfluousElseBreak, ]) { - flake8_return::rules::function(checker, body, returns.as_ref().map(AsRef::as_ref)); + flake8_return::rules::function( + checker, + body, + decorator_list, + returns.as_ref().map(AsRef::as_ref), + ); } if checker.enabled(Rule::UselessReturn) { pylint::rules::useless_return( diff --git a/crates/ruff_linter/src/rules/flake8_return/rules/function.rs b/crates/ruff_linter/src/rules/flake8_return/rules/function.rs index 124abdfee953e9..14ee2b422c8e19 100644 --- a/crates/ruff_linter/src/rules/flake8_return/rules/function.rs +++ b/crates/ruff_linter/src/rules/flake8_return/rules/function.rs @@ -9,7 +9,7 @@ use ruff_python_ast::helpers::{is_const_false, is_const_true}; use ruff_python_ast::stmt_if::elif_else_range; use ruff_python_ast::visitor::Visitor; use ruff_python_ast::whitespace::indentation; -use ruff_python_ast::{self as ast, ElifElseClause, Expr, Stmt}; +use ruff_python_ast::{self as ast, Decorator, ElifElseClause, Expr, Stmt}; use ruff_python_codegen::Stylist; use ruff_python_index::Indexer; use ruff_python_semantic::SemanticModel; @@ -731,7 +731,12 @@ fn superfluous_elif_else(checker: &mut Checker, stack: &Stack) { } /// Run all checks from the `flake8-return` plugin. -pub(crate) fn function(checker: &mut Checker, body: &[Stmt], returns: Option<&Expr>) { +pub(crate) fn function( + checker: &mut Checker, + body: &[Stmt], + decorator_list: &[Decorator], + returns: Option<&Expr>, +) { // Find the last statement in the function. let Some(last_stmt) = body.last() else { // Skip empty functions. @@ -785,6 +790,14 @@ pub(crate) fn function(checker: &mut Checker, body: &[Stmt], returns: Option<&Ex } } else { if checker.enabled(Rule::UnnecessaryReturnNone) { + // Skip properties. + let semantic = checker.semantic(); + if decorator_list + .iter() + .any(|decorator| semantic.match_builtin_expr(&decorator.expression, "property")) + { + return; + } // Skip functions that have a return annotation that is not `None`. if returns.map_or(true, Expr::is_none_literal_expr) { unnecessary_return_none(checker, &stack);