Skip to content

Commit 6391de8

Browse files
authored
[flake8-return] Stabilize only add return None at the end when fixing implicit-return (RET503) (#18516)
This involved slightly more code changes than usual for a stabilization - so maybe worth double-checking the logic! I did verify by hand that the new stable behavior on the test fixture matches the old preview behavior, even after the internal refactor.
1 parent 4cfaa39 commit 6391de8

File tree

5 files changed

+150
-626
lines changed

5 files changed

+150
-626
lines changed

crates/ruff_linter/src/preview.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@ pub(crate) const fn is_bad_version_info_in_non_stub_enabled(settings: &LinterSet
4040
settings.preview.is_enabled()
4141
}
4242

43-
// https://github.com/astral-sh/ruff/pull/11074
44-
pub(crate) const fn is_only_add_return_none_at_end_enabled(settings: &LinterSettings) -> bool {
45-
settings.preview.is_enabled()
46-
}
47-
4843
// https://github.com/astral-sh/ruff/pull/16719
4944
pub(crate) const fn is_fix_manual_dict_comprehension_enabled(settings: &LinterSettings) -> bool {
5045
settings.preview.is_enabled()

crates/ruff_linter/src/rules/flake8_return/mod.rs

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,10 @@ mod tests {
1111
use anyhow::Result;
1212
use test_case::test_case;
1313

14+
use crate::assert_messages;
1415
use crate::registry::Rule;
1516
use crate::settings::LinterSettings;
16-
use crate::settings::types::PreviewMode;
1717
use crate::test::test_path;
18-
use crate::{assert_messages, settings};
1918

2019
#[test_case(Rule::UnnecessaryReturnNone, Path::new("RET501.py"))]
2120
#[test_case(Rule::ImplicitReturnValue, Path::new("RET502.py"))]
@@ -34,22 +33,4 @@ mod tests {
3433
assert_messages!(snapshot, diagnostics);
3534
Ok(())
3635
}
37-
38-
#[test_case(Rule::ImplicitReturn, Path::new("RET503.py"))]
39-
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
40-
let snapshot = format!(
41-
"preview__{}_{}",
42-
rule_code.noqa_code(),
43-
path.to_string_lossy()
44-
);
45-
let diagnostics = test_path(
46-
Path::new("flake8_return").join(path).as_path(),
47-
&settings::LinterSettings {
48-
preview: PreviewMode::Enabled,
49-
..settings::LinterSettings::for_rule(rule_code)
50-
},
51-
)?;
52-
assert_messages!(snapshot, diagnostics);
53-
Ok(())
54-
}
5536
}

crates/ruff_linter/src/rules/flake8_return/rules/function.rs

Lines changed: 30 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ use ruff_text_size::{Ranged, TextRange, TextSize};
1717
use crate::checkers::ast::Checker;
1818
use crate::fix::edits;
1919
use crate::fix::edits::adjust_indentation;
20-
use crate::preview::is_only_add_return_none_at_end_enabled;
2120
use crate::registry::Rule;
2221
use crate::rules::flake8_return::helpers::end_of_last_statement;
2322
use crate::{AlwaysFixableViolation, FixAvailability, Violation};
@@ -463,96 +462,70 @@ fn add_return_none(checker: &Checker, stmt: &Stmt, range: TextRange) {
463462
}
464463
}
465464

466-
/// Returns a list of all implicit returns in the given statement.
467-
///
468-
/// Note: The function should be refactored to `has_implicit_return` with an early return (when seeing the first implicit return)
469-
/// when removing the preview gating.
470-
fn implicit_returns<'a>(checker: &Checker, stmt: &'a Stmt) -> Vec<&'a Stmt> {
465+
fn has_implicit_return(checker: &Checker, stmt: &Stmt) -> bool {
471466
match stmt {
472467
Stmt::If(ast::StmtIf {
473468
body,
474469
elif_else_clauses,
475470
..
476471
}) => {
477-
let mut implicit_stmts = body
472+
if body
478473
.last()
479-
.map(|last| implicit_returns(checker, last))
480-
.unwrap_or_default();
481-
482-
for clause in elif_else_clauses {
483-
implicit_stmts.extend(
484-
clause
485-
.body
486-
.last()
487-
.iter()
488-
.flat_map(|last| implicit_returns(checker, last)),
489-
);
474+
.is_some_and(|last| has_implicit_return(checker, last))
475+
{
476+
return true;
477+
}
478+
479+
if elif_else_clauses.iter().any(|clause| {
480+
clause
481+
.body
482+
.last()
483+
.is_some_and(|last| has_implicit_return(checker, last))
484+
}) {
485+
return true;
490486
}
491487

492488
// Check if we don't have an else clause
493-
if matches!(
489+
matches!(
494490
elif_else_clauses.last(),
495491
None | Some(ast::ElifElseClause { test: Some(_), .. })
496-
) {
497-
implicit_stmts.push(stmt);
498-
}
499-
implicit_stmts
492+
)
500493
}
501-
Stmt::Assert(ast::StmtAssert { test, .. }) if is_const_false(test) => vec![],
502-
Stmt::While(ast::StmtWhile { test, .. }) if is_const_true(test) => vec![],
494+
Stmt::Assert(ast::StmtAssert { test, .. }) if is_const_false(test) => false,
495+
Stmt::While(ast::StmtWhile { test, .. }) if is_const_true(test) => false,
503496
Stmt::For(ast::StmtFor { orelse, .. }) | Stmt::While(ast::StmtWhile { orelse, .. }) => {
504497
if let Some(last_stmt) = orelse.last() {
505-
implicit_returns(checker, last_stmt)
498+
has_implicit_return(checker, last_stmt)
506499
} else {
507-
vec![stmt]
508-
}
509-
}
510-
Stmt::Match(ast::StmtMatch { cases, .. }) => {
511-
let mut implicit_stmts = vec![];
512-
for case in cases {
513-
implicit_stmts.extend(
514-
case.body
515-
.last()
516-
.into_iter()
517-
.flat_map(|last_stmt| implicit_returns(checker, last_stmt)),
518-
);
500+
true
519501
}
520-
implicit_stmts
521502
}
503+
Stmt::Match(ast::StmtMatch { cases, .. }) => cases.iter().any(|case| {
504+
case.body
505+
.last()
506+
.is_some_and(|last| has_implicit_return(checker, last))
507+
}),
522508
Stmt::With(ast::StmtWith { body, .. }) => body
523509
.last()
524-
.map(|last_stmt| implicit_returns(checker, last_stmt))
525-
.unwrap_or_default(),
526-
Stmt::Return(_) | Stmt::Raise(_) | Stmt::Try(_) => vec![],
510+
.is_some_and(|last_stmt| has_implicit_return(checker, last_stmt)),
511+
Stmt::Return(_) | Stmt::Raise(_) | Stmt::Try(_) => false,
527512
Stmt::Expr(ast::StmtExpr { value, .. })
528513
if matches!(
529514
value.as_ref(),
530515
Expr::Call(ast::ExprCall { func, .. })
531516
if is_noreturn_func(func, checker.semantic())
532517
) =>
533518
{
534-
vec![]
535-
}
536-
_ => {
537-
vec![stmt]
519+
false
538520
}
521+
_ => true,
539522
}
540523
}
541524

542525
/// RET503
543526
fn implicit_return(checker: &Checker, function_def: &ast::StmtFunctionDef, stmt: &Stmt) {
544-
let implicit_stmts = implicit_returns(checker, stmt);
545-
546-
if implicit_stmts.is_empty() {
547-
return;
548-
}
549-
550-
if is_only_add_return_none_at_end_enabled(checker.settings) {
527+
if has_implicit_return(checker, stmt) {
551528
add_return_none(checker, stmt, function_def.range());
552-
} else {
553-
for implicit_stmt in implicit_stmts {
554-
add_return_none(checker, implicit_stmt, implicit_stmt.range());
555-
}
556529
}
557530
}
558531

0 commit comments

Comments
 (0)