diff --git a/README.md b/README.md
index 233ae3b6f0..485035dd61 100644
--- a/README.md
+++ b/README.md
@@ -1343,6 +1343,7 @@ For more, see [Pylint](https://pypi.org/project/pylint/) on PyPI.
| PLR0133 | comparison-of-constant | Two constants compared in a comparison, consider replacing `{left_constant} {op} {right_constant}` | |
| PLR0206 | property-with-parameters | Cannot have defined parameters for properties | |
| PLR0402 | consider-using-from-import | Use `from {module} import {name}` in lieu of alias | 🛠|
+| PLR0911 | too-many-return-statements | Too many return statements ({returns}/{max_returns}) | |
| PLR0912 | too-many-branches | Too many branches ({branches}/{max_branches}) | |
| PLR0913 | too-many-arguments | Too many arguments to function call ({c_args}/{max_args}) | |
| PLR0915 | too-many-statements | Too many statements ({statements}/{max_statements}) | |
@@ -3775,6 +3776,23 @@ max-branches = 12
---
+#### [`max-returns`](#max-returns)
+
+Maximum number of return statements allowed for a function or method body (see `PLR0911`)
+
+**Default value**: `6`
+
+**Type**: `int`
+
+**Example usage**:
+
+```toml
+[tool.ruff.pylint]
+max-returns = 6
+```
+
+---
+
#### [`max-statements`](#max-statements)
Maximum number of statements allowed for a function or method body (see: `PLR0915`).
diff --git a/resources/test/fixtures/pylint/too_many_return_statements.py b/resources/test/fixtures/pylint/too_many_return_statements.py
new file mode 100644
index 0000000000..a2241be7e1
--- /dev/null
+++ b/resources/test/fixtures/pylint/too_many_return_statements.py
@@ -0,0 +1,41 @@
+"""
+https://github.com/PyCQA/pylint/blob/69eca9b3f9856c3033957b769358803ee48e8e47/tests/functional/t/too/too_many_return_statements.py
+"""
+def stupid_function(arg): # [too-many-return-statements]
+ if arg == 1:
+ return 1
+ if arg == 2:
+ return 2
+ if arg == 3:
+ return 3
+ if arg == 4:
+ return 4
+ if arg == 5:
+ return 5
+ if arg == 6:
+ return 6
+ if arg == 7:
+ return 7
+ if arg == 8:
+ return 8
+ if arg == 9:
+ return 9
+ if arg == 10:
+ return 10
+ return None
+
+
+def many_yield(text):
+ """Not a problem"""
+ if text:
+ yield f" line 1: {text}\n"
+ yield " line 2\n"
+ yield " line 3\n"
+ yield " line 4\n"
+ yield " line 5\n"
+ else:
+ yield " line 6\n"
+ yield " line 7\n"
+ yield " line 8\n"
+ yield " line 9\n"
+ yield " line 10\n"
\ No newline at end of file
diff --git a/resources/test/fixtures/pylint/too_many_return_statements_params.py b/resources/test/fixtures/pylint/too_many_return_statements_params.py
new file mode 100644
index 0000000000..abbd52101a
--- /dev/null
+++ b/resources/test/fixtures/pylint/too_many_return_statements_params.py
@@ -0,0 +1,5 @@
+def f(x): # Too many return statements (2/1)
+ if x == 1:
+ return
+ if x == 2:
+ return
diff --git a/ruff.schema.json b/ruff.schema.json
index d3b222eb00..b667e0594f 100644
--- a/ruff.schema.json
+++ b/ruff.schema.json
@@ -1199,6 +1199,15 @@
"format": "uint",
"minimum": 0.0
},
+ "max-returns": {
+ "description": "Maximum number of return statements allowed for a function or method body (see `PLR0911`)",
+ "type": [
+ "integer",
+ "null"
+ ],
+ "format": "uint",
+ "minimum": 0.0
+ },
"max-statements": {
"description": "Maximum number of statements allowed for a function or method body (see: `PLR0915`).",
"type": [
@@ -1678,6 +1687,7 @@
"PLR0402",
"PLR09",
"PLR091",
+ "PLR0911",
"PLR0912",
"PLR0913",
"PLR0915",
diff --git a/src/ast/helpers.rs b/src/ast/helpers.rs
index 24c59bab25..a8c4d88596 100644
--- a/src/ast/helpers.rs
+++ b/src/ast/helpers.rs
@@ -13,6 +13,8 @@ use rustpython_parser::token::StringKind;
use smallvec::smallvec;
use crate::ast::types::{Binding, BindingKind, CallPath, Range};
+use crate::ast::visitor;
+use crate::ast::visitor::Visitor;
use crate::checkers::ast::Checker;
use crate::source_code::{Generator, Indexer, Locator, Stylist};
@@ -664,6 +666,27 @@ pub fn to_call_path(target: &str) -> CallPath {
}
}
+/// A [`Visitor`] that collects all return statements in a function or method.
+#[derive(Default)]
+pub struct ReturnStatementVisitor<'a> {
+ pub returns: Vec