From b14a64256a7346057f3e8ebaf7a4546d338a885b Mon Sep 17 00:00:00 2001 From: InSyncWithFoo Date: Thu, 19 Dec 2024 01:28:23 +0000 Subject: [PATCH 1/5] [`ruff`] Needless `else` clause (`RUF047`) Co-authored-by: Micha Reiser --- .../test/fixtures/ruff/RUF047_for.py | 44 ++++ .../resources/test/fixtures/ruff/RUF047_if.py | 84 +++++++ .../test/fixtures/ruff/RUF047_try.py | 71 ++++++ .../test/fixtures/ruff/RUF047_while.py | 44 ++++ .../src/checkers/ast/analyze/statement.rs | 28 ++- crates/ruff_linter/src/codes.rs | 1 + crates/ruff_linter/src/rules/ruff/mod.rs | 4 + .../ruff_linter/src/rules/ruff/rules/mod.rs | 2 + .../src/rules/ruff/rules/needless_else.rs | 217 ++++++++++++++++++ ..._tests__preview__RUF047_RUF047_for.py.snap | 64 ++++++ ...__tests__preview__RUF047_RUF047_if.py.snap | 158 +++++++++++++ ..._tests__preview__RUF047_RUF047_try.py.snap | 65 ++++++ ...ests__preview__RUF047_RUF047_while.py.snap | 64 ++++++ ruff.schema.json | 1 + 14 files changed, 840 insertions(+), 7 deletions(-) create mode 100644 crates/ruff_linter/resources/test/fixtures/ruff/RUF047_for.py create mode 100644 crates/ruff_linter/resources/test/fixtures/ruff/RUF047_if.py create mode 100644 crates/ruff_linter/resources/test/fixtures/ruff/RUF047_try.py create mode 100644 crates/ruff_linter/resources/test/fixtures/ruff/RUF047_while.py create mode 100644 crates/ruff_linter/src/rules/ruff/rules/needless_else.rs create mode 100644 crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_for.py.snap create mode 100644 crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_if.py.snap create mode 100644 crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_try.py.snap create mode 100644 crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_while.py.snap diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_for.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_for.py new file mode 100644 index 0000000000000..727efe4d15bdd --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_for.py @@ -0,0 +1,44 @@ +for _ in range(0): + loop_body_is_not_checked() + break +else: + pass + + +for this in comment: + belongs_to() # `for` +else: + ... + + +for this in second_comment: + belongs() # to +# `else` +else: + pass + + +for _and in so: + does() +# this +else: + pass + + +for of in course(): + this() +else: + ... # too + + +for of in course(): + this() +else: + ... + # too + +for of in course(): + this() +else: + ... +# this comment does not belong to the else diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_if.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_if.py new file mode 100644 index 0000000000000..fbfdcadcba5e4 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_if.py @@ -0,0 +1,84 @@ +# Errors + +if False: + condition_is_not_evaluated() +else: + pass + + +if this_comment(): + belongs_to() # `if` +else: + ... + + +if elif_is(): + treated() +elif the_same(): + as_if() +else: + pass + + +if this_second_comment(): + belongs() # to + # `if` +else: + pass + + +if of_course(): + this() +else: + ... +# this comment doesn't belong to the if + + +if of_course: this() +else: ... + + +if of_course: + this() # comment +else: ... + + +def nested(): + if a: + b() + else: + ... + + +# No errors + +if this_second_comment(): + belongs() # to + # `else` +else: + pass + + +if this_second_comment(): + belongs() # to +# `else` +else: + pass + + +if of_course(): + this() +else: + ... # too + + +if of_course(): + this() +else: + ... + # comment + + +if of_course: + this() # comment +else: ... # trailing diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_try.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_try.py new file mode 100644 index 0000000000000..bfa3f35710280 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_try.py @@ -0,0 +1,71 @@ +try: + raise try_body_is_not_checked() +except: + pass +else: + pass + + +try: + this() +except comment: + belongs() +except: + to() # `except` +else: + ... + + +try: + this() +except (second, comment): + belongs() # to +# `else` +else: + pass + + +try: + and_so() +except: + does() +# this +else: + ... + + +try: + of_course() +except: + this() +else: + ... # too + +try: + of_course() +except: + this() +else: + ... + # This comment belongs to else +finally: + pass + +try: + of_course() +except: + this() +else: + ... +# This comment belongs to finally +finally: + pass + + +try: + of_course() +except: + this() +else: + ... +# This comment belongs to the statement coming after the else diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_while.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_while.py new file mode 100644 index 0000000000000..2599f7b8a3cb3 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_while.py @@ -0,0 +1,44 @@ +while True: + loop_body_is_not_checked() + break +else: + pass + + +while this_comment: + belongs_to() # `for` +else: + ... + + +while this_second_comment: + belongs() # to +# `else` +else: + pass + + +while and_so: + does() +# this +else: + ... + + +while of_course(): + this() +else: + ... # too + +while of_course(): + this() +else: + ... + # this comment belongs to the else + +while of_course(): + this() +else: + ... +# this comment belongs to the statement coming after the else + diff --git a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs index 5611a307f44df..d5f92b9f293f2 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs @@ -1240,6 +1240,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { if checker.enabled(Rule::IfKeyInDictDel) { ruff::rules::if_key_in_dict_del(checker, if_); } + if checker.enabled(Rule::NeedlessElse) { + ruff::rules::needless_else(checker, if_.into()); + } } Stmt::Assert( assert_stmt @ ast::StmtAssert { @@ -1349,6 +1352,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { if checker.enabled(Rule::AsyncBusyWait) { flake8_async::rules::async_busy_wait(checker, while_stmt); } + if checker.enabled(Rule::NeedlessElse) { + ruff::rules::needless_else(checker, while_stmt.into()); + } } Stmt::For( for_stmt @ ast::StmtFor { @@ -1438,14 +1444,19 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { refurb::rules::for_loop_writes(checker, for_stmt); } } + if checker.enabled(Rule::NeedlessElse) { + ruff::rules::needless_else(checker, for_stmt.into()); + } } - Stmt::Try(ast::StmtTry { - body, - handlers, - orelse, - finalbody, - .. - }) => { + Stmt::Try( + try_stmt @ ast::StmtTry { + body, + handlers, + orelse, + finalbody, + .. + }, + ) => { if checker.enabled(Rule::TooManyNestedBlocks) { pylint::rules::too_many_nested_blocks(checker, stmt); } @@ -1512,6 +1523,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { if checker.enabled(Rule::ErrorInsteadOfException) { tryceratops::rules::error_instead_of_exception(checker, handlers); } + if checker.enabled(Rule::NeedlessElse) { + ruff::rules::needless_else(checker, try_stmt.into()); + } } Stmt::Assign(assign @ ast::StmtAssign { targets, value, .. }) => { if checker.enabled(Rule::SelfOrClsAssignment) { diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index 91123b2724a21..8a8e60a418974 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -997,6 +997,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Ruff, "041") => (RuleGroup::Preview, rules::ruff::rules::UnnecessaryNestedLiteral), (Ruff, "043") => (RuleGroup::Preview, rules::ruff::rules::PytestRaisesAmbiguousPattern), (Ruff, "046") => (RuleGroup::Preview, rules::ruff::rules::UnnecessaryCastToInt), + (Ruff, "047") => (RuleGroup::Preview, rules::ruff::rules::NeedlessElse), (Ruff, "048") => (RuleGroup::Preview, rules::ruff::rules::MapIntVersionParsing), (Ruff, "049") => (RuleGroup::Preview, rules::ruff::rules::DataclassEnum), (Ruff, "051") => (RuleGroup::Preview, rules::ruff::rules::IfKeyInDictDel), diff --git a/crates/ruff_linter/src/rules/ruff/mod.rs b/crates/ruff_linter/src/rules/ruff/mod.rs index 4e117304b28c9..41f3573126899 100644 --- a/crates/ruff_linter/src/rules/ruff/mod.rs +++ b/crates/ruff_linter/src/rules/ruff/mod.rs @@ -423,6 +423,10 @@ mod tests { #[test_case(Rule::UnnecessaryRound, Path::new("RUF057.py"))] #[test_case(Rule::DataclassEnum, Path::new("RUF049.py"))] #[test_case(Rule::StarmapZip, Path::new("RUF058.py"))] + #[test_case(Rule::NeedlessElse, Path::new("RUF047_if.py"))] + #[test_case(Rule::NeedlessElse, Path::new("RUF047_for.py"))] + #[test_case(Rule::NeedlessElse, Path::new("RUF047_while.py"))] + #[test_case(Rule::NeedlessElse, Path::new("RUF047_try.py"))] fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!( "preview__{}_{}", diff --git a/crates/ruff_linter/src/rules/ruff/rules/mod.rs b/crates/ruff_linter/src/rules/ruff/rules/mod.rs index 15ea76f49f828..834709136b75b 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/mod.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/mod.rs @@ -21,6 +21,7 @@ pub(crate) use missing_fstring_syntax::*; pub(crate) use mutable_class_default::*; pub(crate) use mutable_dataclass_default::*; pub(crate) use mutable_fromkeys_value::*; +pub(crate) use needless_else::*; pub(crate) use never_union::*; pub(crate) use none_not_at_end_of_union::*; pub(crate) use parenthesize_chained_operators::*; @@ -75,6 +76,7 @@ mod missing_fstring_syntax; mod mutable_class_default; mod mutable_dataclass_default; mod mutable_fromkeys_value; +mod needless_else; mod never_union; mod none_not_at_end_of_union; mod parenthesize_chained_operators; diff --git a/crates/ruff_linter/src/rules/ruff/rules/needless_else.rs b/crates/ruff_linter/src/rules/ruff/rules/needless_else.rs new file mode 100644 index 0000000000000..0fd52a5c9045f --- /dev/null +++ b/crates/ruff_linter/src/rules/ruff/rules/needless_else.rs @@ -0,0 +1,217 @@ +use std::cmp::Ordering; + +use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; +use ruff_macros::{derive_message_formats, ViolationMetadata}; +use ruff_python_ast::helpers::comment_indentation_after; +use ruff_python_ast::whitespace::indentation; +use ruff_python_ast::{Stmt, StmtExpr, StmtFor, StmtIf, StmtTry, StmtWhile}; +use ruff_python_parser::{TokenKind, Tokens}; +use ruff_source_file::LineRanges; +use ruff_text_size::{Ranged, TextLen, TextRange}; + +use crate::checkers::ast::Checker; + +/// ## What it does +/// Checks for `else` clauses that only contains `pass` and `...` statements. +/// +/// ## Why is this bad? +/// Such an else clause does nothing and can be removed. +/// +/// ## Example +/// ```python +/// if foo: +/// bar() +/// else: +/// pass +/// ``` +/// +/// Use instead: +/// ```python +/// if foo: +/// bar() +/// ``` +#[derive(ViolationMetadata)] +pub(crate) struct NeedlessElse; + +impl AlwaysFixableViolation for NeedlessElse { + #[derive_message_formats] + fn message(&self) -> String { + "Empty `else` clause".to_string() + } + + fn fix_title(&self) -> String { + "Remove `else` clause".to_string() + } +} + +/// RUF047 +pub(crate) fn needless_else(checker: &mut Checker, stmt: AnyNodeWithOrElse) { + let source = checker.source(); + let tokens = checker.tokens(); + + let else_body = stmt.else_body(); + + if !body_is_no_op(else_body) { + return; + } + + let Some(else_range) = stmt.else_range(tokens) else { + return; + }; + + let Some(preceding_stmt) = stmt.body_before_else().last() else { + return; + }; + + let before_else_full_end = source.full_line_end(preceding_stmt.end()); + let else_start = else_range.start(); + let else_full_end = source.full_line_end(else_range.end()); + + for token in tokens.in_range(TextRange::new(before_else_full_end, else_start)) { + if token.kind() != TokenKind::Comment { + continue; + } + + let comment_indentation = + comment_indentation_after(preceding_stmt.into(), token.range(), source); + + let preceding_indentation = indentation(source, &preceding_stmt) + .unwrap_or_default() + .text_len(); + + match comment_indentation.cmp(&preceding_indentation) { + Ordering::Greater => continue, + Ordering::Equal => return, + Ordering::Less => return, + } + } + + if checker + .comment_ranges() + .intersects(TextRange::new(else_range.start(), else_full_end)) + { + return; + } + + let else_line_start = source.line_start(else_range.start()); + let remove_range = TextRange::new(else_line_start, else_full_end); + + let edit = Edit::range_deletion(remove_range); + let fix = Fix::safe_edit(edit); + + let diagnostic = Diagnostic::new(NeedlessElse, else_range); + + checker.diagnostics.push(diagnostic.with_fix(fix)); +} + +/// Whether `body` contains only one `pass` or `...` statement. +fn body_is_no_op(body: &[Stmt]) -> bool { + match body { + [Stmt::Pass(_)] => true, + [Stmt::Expr(StmtExpr { value, .. })] => value.is_ellipsis_literal_expr(), + _ => false, + } +} + +#[derive(Copy, Clone, Debug)] +pub(crate) enum AnyNodeWithOrElse<'a> { + While(&'a StmtWhile), + For(&'a StmtFor), + Try(&'a StmtTry), + If(&'a StmtIf), +} + +impl<'a> AnyNodeWithOrElse<'a> { + /// Returns the range from the `else` keyword to the last statement in its block. + fn else_range(self, tokens: &Tokens) -> Option { + match self { + Self::For(_) | Self::While(_) | Self::Try(_) => { + let before_else = self.body_before_else(); + + let else_body = self.else_body(); + let end = else_body.last()?.end(); + + let start = tokens + .in_range(TextRange::new(before_else.last()?.end(), end)) + .iter() + .find(|token| token.kind() == TokenKind::Else)? + .start(); + + Some(TextRange::new(start, end)) + } + + Self::If(StmtIf { + elif_else_clauses, .. + }) => elif_else_clauses + .last() + .filter(|clause| clause.test.is_none()) + .map(Ranged::range), + } + } + + /// Returns the suite before the else block. + fn body_before_else(self) -> &'a [Stmt] { + match self { + Self::Try(StmtTry { body, handlers, .. }) => handlers + .last() + .and_then(|handler| handler.as_except_handler()) + .map(|handler| &handler.body) + .unwrap_or(body), + + Self::While(StmtWhile { body, .. }) | Self::For(StmtFor { body, .. }) => body, + + Self::If(StmtIf { + body, + elif_else_clauses, + .. + }) => elif_else_clauses + .iter() + .rev() + .find(|clause| clause.test.is_some()) + .map(|clause| &*clause.body) + .unwrap_or(body), + } + } + + /// Returns the `else` suite. + /// Defaults to an empty suite if the statement has no `else` block. + fn else_body(self) -> &'a [Stmt] { + match self { + Self::While(StmtWhile { orelse, .. }) + | Self::For(StmtFor { orelse, .. }) + | Self::Try(StmtTry { orelse, .. }) => orelse, + + Self::If(StmtIf { + elif_else_clauses, .. + }) => elif_else_clauses + .last() + .filter(|clause| clause.test.is_none()) + .map(|clause| &*clause.body) + .unwrap_or_default(), + } + } +} + +impl<'a> From<&'a StmtFor> for AnyNodeWithOrElse<'a> { + fn from(value: &'a StmtFor) -> Self { + Self::For(value) + } +} + +impl<'a> From<&'a StmtWhile> for AnyNodeWithOrElse<'a> { + fn from(value: &'a StmtWhile) -> Self { + Self::While(value) + } +} + +impl<'a> From<&'a StmtIf> for AnyNodeWithOrElse<'a> { + fn from(value: &'a StmtIf) -> Self { + Self::If(value) + } +} + +impl<'a> From<&'a StmtTry> for AnyNodeWithOrElse<'a> { + fn from(value: &'a StmtTry) -> Self { + Self::Try(value) + } +} diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_for.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_for.py.snap new file mode 100644 index 0000000000000..da5aca1aee3f6 --- /dev/null +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_for.py.snap @@ -0,0 +1,64 @@ +--- +source: crates/ruff_linter/src/rules/ruff/mod.rs +snapshot_kind: text +--- +RUF047_for.py:4:1: RUF047 [*] Empty `else` clause + | +2 | loop_body_is_not_checked() +3 | break +4 | / else: +5 | | pass + | |________^ RUF047 + | + = help: Remove `else` clause + +ℹ Safe fix +1 1 | for _ in range(0): +2 2 | loop_body_is_not_checked() +3 3 | break +4 |-else: +5 |- pass +6 4 | +7 5 | +8 6 | for this in comment: + +RUF047_for.py:10:1: RUF047 [*] Empty `else` clause + | + 8 | for this in comment: + 9 | belongs_to() # `for` +10 | / else: +11 | | ... + | |_______^ RUF047 + | + = help: Remove `else` clause + +ℹ Safe fix +7 7 | +8 8 | for this in comment: +9 9 | belongs_to() # `for` +10 |-else: +11 |- ... +12 10 | +13 11 | +14 12 | for this in second_comment: + +RUF047_for.py:36:1: RUF047 [*] Empty `else` clause + | +34 | for of in course(): +35 | this() +36 | / else: +37 | | ... + | |_______^ RUF047 +38 | # too + | + = help: Remove `else` clause + +ℹ Safe fix +33 33 | +34 34 | for of in course(): +35 35 | this() +36 |-else: +37 |- ... +38 36 | # too +39 37 | +40 38 | for of in course(): diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_if.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_if.py.snap new file mode 100644 index 0000000000000..c86baecb0de22 --- /dev/null +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_if.py.snap @@ -0,0 +1,158 @@ +--- +source: crates/ruff_linter/src/rules/ruff/mod.rs +--- +RUF047_if.py:5:1: RUF047 [*] Empty `else` clause + | +3 | if False: +4 | condition_is_not_evaluated() +5 | / else: +6 | | pass + | |________^ RUF047 + | + = help: Remove `else` clause + +ℹ Safe fix +2 2 | +3 3 | if False: +4 4 | condition_is_not_evaluated() +5 |-else: +6 |- pass +7 5 | +8 6 | +9 7 | if this_comment(): + +RUF047_if.py:11:1: RUF047 [*] Empty `else` clause + | + 9 | if this_comment(): +10 | belongs_to() # `if` +11 | / else: +12 | | ... + | |_______^ RUF047 + | + = help: Remove `else` clause + +ℹ Safe fix +8 8 | +9 9 | if this_comment(): +10 10 | belongs_to() # `if` +11 |-else: +12 |- ... +13 11 | +14 12 | +15 13 | if elif_is(): + +RUF047_if.py:19:1: RUF047 [*] Empty `else` clause + | +17 | elif the_same(): +18 | as_if() +19 | / else: +20 | | pass + | |________^ RUF047 + | + = help: Remove `else` clause + +ℹ Safe fix +16 16 | treated() +17 17 | elif the_same(): +18 18 | as_if() +19 |-else: +20 |- pass +21 19 | +22 20 | +23 21 | if this_second_comment(): + +RUF047_if.py:26:1: RUF047 [*] Empty `else` clause + | +24 | belongs() # to +25 | # `if` +26 | / else: +27 | | pass + | |________^ RUF047 + | + = help: Remove `else` clause + +ℹ Safe fix +23 23 | if this_second_comment(): +24 24 | belongs() # to +25 25 | # `if` +26 |-else: +27 |- pass +28 26 | +29 27 | +30 28 | if of_course(): + +RUF047_if.py:38:1: RUF047 [*] Empty `else` clause + | +37 | if of_course: this() +38 | else: ... + | ^^^^^^^^^ RUF047 + | + = help: Remove `else` clause + +ℹ Safe fix +35 35 | +36 36 | +37 37 | if of_course: this() +38 |-else: ... +39 38 | +40 39 | +41 40 | if of_course: + +RUF047_if.py:43:1: RUF047 [*] Empty `else` clause + | +41 | if of_course: +42 | this() # comment +43 | else: ... + | ^^^^^^^^^ RUF047 + | + = help: Remove `else` clause + +ℹ Safe fix +40 40 | +41 41 | if of_course: +42 42 | this() # comment +43 |-else: ... +44 43 | +45 44 | +46 45 | def nested(): + +RUF047_if.py:49:5: RUF047 [*] Empty `else` clause + | +47 | if a: +48 | b() +49 | / else: +50 | | ... + | |___________^ RUF047 + | + = help: Remove `else` clause + +ℹ Safe fix +46 46 | def nested(): +47 47 | if a: +48 48 | b() +49 |- else: +50 |- ... +51 49 | +52 50 | +53 51 | # No errors + +RUF047_if.py:77:1: RUF047 [*] Empty `else` clause + | +75 | if of_course(): +76 | this() +77 | / else: +78 | | ... + | |_______^ RUF047 +79 | # comment + | + = help: Remove `else` clause + +ℹ Safe fix +74 74 | +75 75 | if of_course(): +76 76 | this() +77 |-else: +78 |- ... +79 77 | # comment +80 78 | +81 79 | diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_try.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_try.py.snap new file mode 100644 index 0000000000000..bf32be69eb4ac --- /dev/null +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_try.py.snap @@ -0,0 +1,65 @@ +--- +source: crates/ruff_linter/src/rules/ruff/mod.rs +snapshot_kind: text +--- +RUF047_try.py:5:1: RUF047 [*] Empty `else` clause + | +3 | except: +4 | pass +5 | / else: +6 | | pass + | |________^ RUF047 + | + = help: Remove `else` clause + +ℹ Safe fix +2 2 | raise try_body_is_not_checked() +3 3 | except: +4 4 | pass +5 |-else: +6 |- pass +7 5 | +8 6 | +9 7 | try: + +RUF047_try.py:15:1: RUF047 [*] Empty `else` clause + | +13 | except: +14 | to() # `except` +15 | / else: +16 | | ... + | |_______^ RUF047 + | + = help: Remove `else` clause + +ℹ Safe fix +12 12 | belongs() +13 13 | except: +14 14 | to() # `except` +15 |-else: +16 |- ... +17 15 | +18 16 | +19 17 | try: + +RUF047_try.py:48:1: RUF047 [*] Empty `else` clause + | +46 | except: +47 | this() +48 | / else: +49 | | ... + | |_______^ RUF047 +50 | # This comment belongs to else +51 | finally: + | + = help: Remove `else` clause + +ℹ Safe fix +45 45 | of_course() +46 46 | except: +47 47 | this() +48 |-else: +49 |- ... +50 48 | # This comment belongs to else +51 49 | finally: +52 50 | pass diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_while.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_while.py.snap new file mode 100644 index 0000000000000..5c29e8e4d13e8 --- /dev/null +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_while.py.snap @@ -0,0 +1,64 @@ +--- +source: crates/ruff_linter/src/rules/ruff/mod.rs +snapshot_kind: text +--- +RUF047_while.py:4:1: RUF047 [*] Empty `else` clause + | +2 | loop_body_is_not_checked() +3 | break +4 | / else: +5 | | pass + | |________^ RUF047 + | + = help: Remove `else` clause + +ℹ Safe fix +1 1 | while True: +2 2 | loop_body_is_not_checked() +3 3 | break +4 |-else: +5 |- pass +6 4 | +7 5 | +8 6 | while this_comment: + +RUF047_while.py:10:1: RUF047 [*] Empty `else` clause + | + 8 | while this_comment: + 9 | belongs_to() # `for` +10 | / else: +11 | | ... + | |_______^ RUF047 + | + = help: Remove `else` clause + +ℹ Safe fix +7 7 | +8 8 | while this_comment: +9 9 | belongs_to() # `for` +10 |-else: +11 |- ... +12 10 | +13 11 | +14 12 | while this_second_comment: + +RUF047_while.py:35:1: RUF047 [*] Empty `else` clause + | +33 | while of_course(): +34 | this() +35 | / else: +36 | | ... + | |_______^ RUF047 +37 | # this comment belongs to the else + | + = help: Remove `else` clause + +ℹ Safe fix +32 32 | +33 33 | while of_course(): +34 34 | this() +35 |-else: +36 |- ... +37 35 | # this comment belongs to the else +38 36 | +39 37 | while of_course(): diff --git a/ruff.schema.json b/ruff.schema.json index 7469867d0197c..313a06de62db0 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -3921,6 +3921,7 @@ "RUF041", "RUF043", "RUF046", + "RUF047", "RUF048", "RUF049", "RUF05", From 02fff51e797198e948de3624696aa85e796b80b2 Mon Sep 17 00:00:00 2001 From: InSyncWithFoo Date: Mon, 20 Jan 2025 14:53:13 +0000 Subject: [PATCH 2/5] Update snapshots --- .../test/fixtures/ruff/RUF047_for.py | 17 ++-- .../resources/test/fixtures/ruff/RUF047_if.py | 4 +- .../test/fixtures/ruff/RUF047_try.py | 43 +++++----- .../test/fixtures/ruff/RUF047_while.py | 17 ++-- ..._tests__preview__RUF047_RUF047_for.py.snap | 79 +++++++++--------- ...__tests__preview__RUF047_RUF047_if.py.snap | 2 +- ..._tests__preview__RUF047_RUF047_try.py.snap | 83 +++++++++---------- ...ests__preview__RUF047_RUF047_while.py.snap | 80 +++++++++--------- 8 files changed, 167 insertions(+), 158 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_for.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_for.py index 727efe4d15bdd..9710fefd99e2d 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_for.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_for.py @@ -1,3 +1,5 @@ +### Errors + for _ in range(0): loop_body_is_not_checked() break @@ -11,6 +13,15 @@ ... +for of in course(): + this() +else: + ... +# this comment does not belong to the else + + +### No errors + for this in second_comment: belongs() # to # `else` @@ -36,9 +47,3 @@ else: ... # too - -for of in course(): - this() -else: - ... -# this comment does not belong to the else diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_if.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_if.py index fbfdcadcba5e4..ff7b0001375f0 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_if.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_if.py @@ -1,4 +1,4 @@ -# Errors +### Errors if False: condition_is_not_evaluated() @@ -50,7 +50,7 @@ def nested(): ... -# No errors +### No errors if this_second_comment(): belongs() # to diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_try.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_try.py index bfa3f35710280..d0b1e40cad6e4 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_try.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_try.py @@ -1,3 +1,5 @@ +### Errors + try: raise try_body_is_not_checked() except: @@ -16,6 +18,28 @@ ... +try: + of_course() +except: + this() +else: + ... +# This comment belongs to finally +finally: + pass + + +try: + of_course() +except: + this() +else: + ... +# This comment belongs to the statement coming after the else + + +### No errors + try: this() except (second, comment): @@ -50,22 +74,3 @@ # This comment belongs to else finally: pass - -try: - of_course() -except: - this() -else: - ... -# This comment belongs to finally -finally: - pass - - -try: - of_course() -except: - this() -else: - ... -# This comment belongs to the statement coming after the else diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_while.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_while.py index 2599f7b8a3cb3..2b106a00cc236 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_while.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_while.py @@ -1,3 +1,5 @@ +### No errors + while True: loop_body_is_not_checked() break @@ -11,6 +13,15 @@ ... +while of_course(): + this() +else: + ... +# this comment belongs to the statement coming after the else + + +### No errors + while this_second_comment: belongs() # to # `else` @@ -36,9 +47,3 @@ ... # this comment belongs to the else -while of_course(): - this() -else: - ... -# this comment belongs to the statement coming after the else - diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_for.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_for.py.snap index da5aca1aee3f6..03d3970ec230b 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_for.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_for.py.snap @@ -1,64 +1,61 @@ --- source: crates/ruff_linter/src/rules/ruff/mod.rs -snapshot_kind: text --- -RUF047_for.py:4:1: RUF047 [*] Empty `else` clause +RUF047_for.py:6:1: RUF047 [*] Empty `else` clause | -2 | loop_body_is_not_checked() -3 | break -4 | / else: -5 | | pass +4 | loop_body_is_not_checked() +5 | break +6 | / else: +7 | | pass | |________^ RUF047 | = help: Remove `else` clause ℹ Safe fix -1 1 | for _ in range(0): -2 2 | loop_body_is_not_checked() -3 3 | break -4 |-else: -5 |- pass -6 4 | -7 5 | -8 6 | for this in comment: +3 3 | for _ in range(0): +4 4 | loop_body_is_not_checked() +5 5 | break +6 |-else: +7 |- pass +8 6 | +9 7 | +10 8 | for this in comment: -RUF047_for.py:10:1: RUF047 [*] Empty `else` clause +RUF047_for.py:12:1: RUF047 [*] Empty `else` clause | - 8 | for this in comment: - 9 | belongs_to() # `for` -10 | / else: -11 | | ... +10 | for this in comment: +11 | belongs_to() # `for` +12 | / else: +13 | | ... | |_______^ RUF047 | = help: Remove `else` clause ℹ Safe fix -7 7 | -8 8 | for this in comment: -9 9 | belongs_to() # `for` -10 |-else: -11 |- ... -12 10 | -13 11 | -14 12 | for this in second_comment: +9 9 | +10 10 | for this in comment: +11 11 | belongs_to() # `for` +12 |-else: +13 |- ... +14 12 | +15 13 | +16 14 | for of in course(): -RUF047_for.py:36:1: RUF047 [*] Empty `else` clause +RUF047_for.py:47:1: RUF047 [*] Empty `else` clause | -34 | for of in course(): -35 | this() -36 | / else: -37 | | ... +45 | for of in course(): +46 | this() +47 | / else: +48 | | ... | |_______^ RUF047 -38 | # too +49 | # too | = help: Remove `else` clause ℹ Safe fix -33 33 | -34 34 | for of in course(): -35 35 | this() -36 |-else: -37 |- ... -38 36 | # too -39 37 | -40 38 | for of in course(): +44 44 | +45 45 | for of in course(): +46 46 | this() +47 |-else: +48 |- ... +49 47 | # too diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_if.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_if.py.snap index c86baecb0de22..f45f8afe83726 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_if.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_if.py.snap @@ -134,7 +134,7 @@ RUF047_if.py:49:5: RUF047 [*] Empty `else` clause 50 |- ... 51 49 | 52 50 | -53 51 | # No errors +53 51 | ### No errors RUF047_if.py:77:1: RUF047 [*] Empty `else` clause | diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_try.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_try.py.snap index bf32be69eb4ac..44adebe21e3d8 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_try.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_try.py.snap @@ -1,65 +1,64 @@ --- source: crates/ruff_linter/src/rules/ruff/mod.rs -snapshot_kind: text --- -RUF047_try.py:5:1: RUF047 [*] Empty `else` clause +RUF047_try.py:7:1: RUF047 [*] Empty `else` clause | -3 | except: -4 | pass -5 | / else: -6 | | pass +5 | except: +6 | pass +7 | / else: +8 | | pass | |________^ RUF047 | = help: Remove `else` clause ℹ Safe fix -2 2 | raise try_body_is_not_checked() -3 3 | except: -4 4 | pass -5 |-else: -6 |- pass -7 5 | -8 6 | -9 7 | try: +4 4 | raise try_body_is_not_checked() +5 5 | except: +6 6 | pass +7 |-else: +8 |- pass +9 7 | +10 8 | +11 9 | try: -RUF047_try.py:15:1: RUF047 [*] Empty `else` clause +RUF047_try.py:17:1: RUF047 [*] Empty `else` clause | -13 | except: -14 | to() # `except` -15 | / else: -16 | | ... +15 | except: +16 | to() # `except` +17 | / else: +18 | | ... | |_______^ RUF047 | = help: Remove `else` clause ℹ Safe fix -12 12 | belongs() -13 13 | except: -14 14 | to() # `except` -15 |-else: -16 |- ... -17 15 | -18 16 | -19 17 | try: +14 14 | belongs() +15 15 | except: +16 16 | to() # `except` +17 |-else: +18 |- ... +19 17 | +20 18 | +21 19 | try: -RUF047_try.py:48:1: RUF047 [*] Empty `else` clause +RUF047_try.py:72:1: RUF047 [*] Empty `else` clause | -46 | except: -47 | this() -48 | / else: -49 | | ... +70 | except: +71 | this() +72 | / else: +73 | | ... | |_______^ RUF047 -50 | # This comment belongs to else -51 | finally: +74 | # This comment belongs to else +75 | finally: | = help: Remove `else` clause ℹ Safe fix -45 45 | of_course() -46 46 | except: -47 47 | this() -48 |-else: -49 |- ... -50 48 | # This comment belongs to else -51 49 | finally: -52 50 | pass +69 69 | of_course() +70 70 | except: +71 71 | this() +72 |-else: +73 |- ... +74 72 | # This comment belongs to else +75 73 | finally: +76 74 | pass diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_while.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_while.py.snap index 5c29e8e4d13e8..b48f9694b9b5b 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_while.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_while.py.snap @@ -1,64 +1,62 @@ --- source: crates/ruff_linter/src/rules/ruff/mod.rs -snapshot_kind: text --- -RUF047_while.py:4:1: RUF047 [*] Empty `else` clause +RUF047_while.py:6:1: RUF047 [*] Empty `else` clause | -2 | loop_body_is_not_checked() -3 | break -4 | / else: -5 | | pass +4 | loop_body_is_not_checked() +5 | break +6 | / else: +7 | | pass | |________^ RUF047 | = help: Remove `else` clause ℹ Safe fix -1 1 | while True: -2 2 | loop_body_is_not_checked() -3 3 | break -4 |-else: -5 |- pass -6 4 | -7 5 | -8 6 | while this_comment: +3 3 | while True: +4 4 | loop_body_is_not_checked() +5 5 | break +6 |-else: +7 |- pass +8 6 | +9 7 | +10 8 | while this_comment: -RUF047_while.py:10:1: RUF047 [*] Empty `else` clause +RUF047_while.py:12:1: RUF047 [*] Empty `else` clause | - 8 | while this_comment: - 9 | belongs_to() # `for` -10 | / else: -11 | | ... +10 | while this_comment: +11 | belongs_to() # `for` +12 | / else: +13 | | ... | |_______^ RUF047 | = help: Remove `else` clause ℹ Safe fix -7 7 | -8 8 | while this_comment: -9 9 | belongs_to() # `for` -10 |-else: -11 |- ... -12 10 | -13 11 | -14 12 | while this_second_comment: +9 9 | +10 10 | while this_comment: +11 11 | belongs_to() # `for` +12 |-else: +13 |- ... +14 12 | +15 13 | +16 14 | while of_course(): -RUF047_while.py:35:1: RUF047 [*] Empty `else` clause +RUF047_while.py:46:1: RUF047 [*] Empty `else` clause | -33 | while of_course(): -34 | this() -35 | / else: -36 | | ... +44 | while of_course(): +45 | this() +46 | / else: +47 | | ... | |_______^ RUF047 -37 | # this comment belongs to the else +48 | # this comment belongs to the else | = help: Remove `else` clause ℹ Safe fix -32 32 | -33 33 | while of_course(): -34 34 | this() -35 |-else: -36 |- ... -37 35 | # this comment belongs to the else -38 36 | -39 37 | while of_course(): +43 43 | +44 44 | while of_course(): +45 45 | this() +46 |-else: +47 |- ... +48 46 | # this comment belongs to the else +49 47 | From 68ea7a9bdf1afcc364b1c6bb23f1965aeed5faa5 Mon Sep 17 00:00:00 2001 From: InSyncWithFoo Date: Tue, 21 Jan 2025 03:01:40 +0000 Subject: [PATCH 3/5] Fix --- .../src/rules/ruff/rules/needless_else.rs | 129 ++++++++++++++---- ..._tests__preview__RUF047_RUF047_for.py.snap | 19 --- ...__tests__preview__RUF047_RUF047_if.py.snap | 21 --- ..._tests__preview__RUF047_RUF047_try.py.snap | 22 --- ...ests__preview__RUF047_RUF047_while.py.snap | 20 --- 5 files changed, 99 insertions(+), 112 deletions(-) diff --git a/crates/ruff_linter/src/rules/ruff/rules/needless_else.rs b/crates/ruff_linter/src/rules/ruff/rules/needless_else.rs index 0fd52a5c9045f..ddf8a293075b3 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/needless_else.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/needless_else.rs @@ -7,7 +7,7 @@ use ruff_python_ast::whitespace::indentation; use ruff_python_ast::{Stmt, StmtExpr, StmtFor, StmtIf, StmtTry, StmtWhile}; use ruff_python_parser::{TokenKind, Tokens}; use ruff_source_file::LineRanges; -use ruff_text_size::{Ranged, TextLen, TextRange}; +use ruff_text_size::{Ranged, TextLen, TextRange, TextSize}; use crate::checkers::ast::Checker; @@ -59,13 +59,74 @@ pub(crate) fn needless_else(checker: &mut Checker, stmt: AnyNodeWithOrElse) { return; }; - let Some(preceding_stmt) = stmt.body_before_else().last() else { + if else_branch_should_not_be_reported(stmt, else_range, checker) { return; + } + + let else_line_start = source.line_start(else_range.start()); + let else_full_end = source.full_line_end(else_range.end()); + let remove_range = TextRange::new(else_line_start, else_full_end); + + let edit = Edit::range_deletion(remove_range); + let fix = Fix::safe_edit(edit); + + let diagnostic = Diagnostic::new(NeedlessElse, else_range); + + checker.diagnostics.push(diagnostic.with_fix(fix)); +} + +/// Whether `body` contains only one `pass` or `...` statement. +fn body_is_no_op(body: &[Stmt]) -> bool { + match body { + [Stmt::Pass(_)] => true, + [Stmt::Expr(StmtExpr { value, .. })] => value.is_ellipsis_literal_expr(), + _ => false, + } +} + +fn else_branch_should_not_be_reported( + stmt: AnyNodeWithOrElse, + else_range: TextRange, + checker: &Checker, +) -> bool { + let Some(preceding_stmt) = stmt.body_before_else().last() else { + return true; }; + let Some(else_last_stmt) = stmt.else_body().last() else { + return true; + }; + + if else_branch_has_preceding_comment(preceding_stmt, else_range, checker) { + return true; + } + + let else_full_end = checker.source().full_line_end(else_range.end()); + let commentable_range = TextRange::new(else_range.start(), else_full_end); + + if checker.comment_ranges().intersects(commentable_range) { + return true; + } + + if else_branch_has_trailing_comment(else_last_stmt, else_full_end, checker) { + return true; + } + + false +} + +fn else_branch_has_preceding_comment( + preceding_stmt: &Stmt, + else_range: TextRange, + checker: &Checker, +) -> bool { + let (tokens, source) = (checker.tokens(), checker.source()); let before_else_full_end = source.full_line_end(preceding_stmt.end()); let else_start = else_range.start(); - let else_full_end = source.full_line_end(else_range.end()); + + let preceding_indentation = indentation(source, &preceding_stmt) + .unwrap_or_default() + .text_len(); for token in tokens.in_range(TextRange::new(before_else_full_end, else_start)) { if token.kind() != TokenKind::Comment { @@ -75,42 +136,50 @@ pub(crate) fn needless_else(checker: &mut Checker, stmt: AnyNodeWithOrElse) { let comment_indentation = comment_indentation_after(preceding_stmt.into(), token.range(), source); - let preceding_indentation = indentation(source, &preceding_stmt) - .unwrap_or_default() - .text_len(); - match comment_indentation.cmp(&preceding_indentation) { Ordering::Greater => continue, - Ordering::Equal => return, - Ordering::Less => return, + Ordering::Equal => return true, + Ordering::Less => return true, } } - if checker - .comment_ranges() - .intersects(TextRange::new(else_range.start(), else_full_end)) - { - return; - } - - let else_line_start = source.line_start(else_range.start()); - let remove_range = TextRange::new(else_line_start, else_full_end); - - let edit = Edit::range_deletion(remove_range); - let fix = Fix::safe_edit(edit); + false +} - let diagnostic = Diagnostic::new(NeedlessElse, else_range); +fn else_branch_has_trailing_comment( + preceding_stmt: &Stmt, + else_full_end: TextSize, + checker: &Checker, +) -> bool { + let (tokens, source) = (checker.tokens(), checker.source()); + + let preceding_indentation = indentation(source, &preceding_stmt) + .unwrap_or_default() + .text_len(); + + for token in tokens.after(else_full_end) { + match token.kind() { + TokenKind::Comment => { + let comment_indentation = + comment_indentation_after(preceding_stmt.into(), token.range(), source); + + match comment_indentation.cmp(&preceding_indentation) { + Ordering::Greater => return true, + Ordering::Equal => return true, + Ordering::Less => break, + } + } - checker.diagnostics.push(diagnostic.with_fix(fix)); -} + TokenKind::NonLogicalNewline + | TokenKind::Newline + | TokenKind::Indent + | TokenKind::Dedent => {} -/// Whether `body` contains only one `pass` or `...` statement. -fn body_is_no_op(body: &[Stmt]) -> bool { - match body { - [Stmt::Pass(_)] => true, - [Stmt::Expr(StmtExpr { value, .. })] => value.is_ellipsis_literal_expr(), - _ => false, + _ => break, + } } + + false } #[derive(Copy, Clone, Debug)] diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_for.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_for.py.snap index 03d3970ec230b..e9b392c6468dd 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_for.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_for.py.snap @@ -40,22 +40,3 @@ RUF047_for.py:12:1: RUF047 [*] Empty `else` clause 14 12 | 15 13 | 16 14 | for of in course(): - -RUF047_for.py:47:1: RUF047 [*] Empty `else` clause - | -45 | for of in course(): -46 | this() -47 | / else: -48 | | ... - | |_______^ RUF047 -49 | # too - | - = help: Remove `else` clause - -ℹ Safe fix -44 44 | -45 45 | for of in course(): -46 46 | this() -47 |-else: -48 |- ... -49 47 | # too diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_if.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_if.py.snap index f45f8afe83726..24428045bda82 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_if.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_if.py.snap @@ -135,24 +135,3 @@ RUF047_if.py:49:5: RUF047 [*] Empty `else` clause 51 49 | 52 50 | 53 51 | ### No errors - -RUF047_if.py:77:1: RUF047 [*] Empty `else` clause - | -75 | if of_course(): -76 | this() -77 | / else: -78 | | ... - | |_______^ RUF047 -79 | # comment - | - = help: Remove `else` clause - -ℹ Safe fix -74 74 | -75 75 | if of_course(): -76 76 | this() -77 |-else: -78 |- ... -79 77 | # comment -80 78 | -81 79 | diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_try.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_try.py.snap index 44adebe21e3d8..3a76ee2750bb6 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_try.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_try.py.snap @@ -40,25 +40,3 @@ RUF047_try.py:17:1: RUF047 [*] Empty `else` clause 19 17 | 20 18 | 21 19 | try: - -RUF047_try.py:72:1: RUF047 [*] Empty `else` clause - | -70 | except: -71 | this() -72 | / else: -73 | | ... - | |_______^ RUF047 -74 | # This comment belongs to else -75 | finally: - | - = help: Remove `else` clause - -ℹ Safe fix -69 69 | of_course() -70 70 | except: -71 71 | this() -72 |-else: -73 |- ... -74 72 | # This comment belongs to else -75 73 | finally: -76 74 | pass diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_while.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_while.py.snap index b48f9694b9b5b..f1f515bf19dbc 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_while.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_while.py.snap @@ -40,23 +40,3 @@ RUF047_while.py:12:1: RUF047 [*] Empty `else` clause 14 12 | 15 13 | 16 14 | while of_course(): - -RUF047_while.py:46:1: RUF047 [*] Empty `else` clause - | -44 | while of_course(): -45 | this() -46 | / else: -47 | | ... - | |_______^ RUF047 -48 | # this comment belongs to the else - | - = help: Remove `else` clause - -ℹ Safe fix -43 43 | -44 44 | while of_course(): -45 45 | this() -46 |-else: -47 |- ... -48 46 | # this comment belongs to the else -49 47 | From 51e3e263e9c1b318aec5e8822e2c08ca360e7748 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Tue, 21 Jan 2025 09:15:07 +0100 Subject: [PATCH 4/5] Nits, fix leading own line comment attribution --- .../resources/test/fixtures/ruff/RUF047_if.py | 14 +-- crates/ruff_linter/src/rules/ruff/mod.rs | 8 +- .../src/rules/ruff/rules/needless_else.rs | 70 ++++++++----- ...s__ruff__tests__RUF047_RUF047_for.py.snap} | 0 ...es__ruff__tests__RUF047_RUF047_if.py.snap} | 98 ++++++++++++------- ...s__ruff__tests__RUF047_RUF047_try.py.snap} | 0 ..._ruff__tests__RUF047_RUF047_while.py.snap} | 0 7 files changed, 115 insertions(+), 75 deletions(-) rename crates/ruff_linter/src/rules/ruff/snapshots/{ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_for.py.snap => ruff_linter__rules__ruff__tests__RUF047_RUF047_for.py.snap} (100%) rename crates/ruff_linter/src/rules/ruff/snapshots/{ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_if.py.snap => ruff_linter__rules__ruff__tests__RUF047_RUF047_if.py.snap} (58%) rename crates/ruff_linter/src/rules/ruff/snapshots/{ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_try.py.snap => ruff_linter__rules__ruff__tests__RUF047_RUF047_try.py.snap} (100%) rename crates/ruff_linter/src/rules/ruff/snapshots/{ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_while.py.snap => ruff_linter__rules__ruff__tests__RUF047_RUF047_while.py.snap} (100%) diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_if.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_if.py index ff7b0001375f0..7b0a1ba275b5a 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_if.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF047_if.py @@ -22,7 +22,13 @@ if this_second_comment(): belongs() # to - # `if` + # `if` +else: + pass + +if this_second_comment(): + belongs() # to + # `if` else: pass @@ -52,12 +58,6 @@ def nested(): ### No errors -if this_second_comment(): - belongs() # to - # `else` -else: - pass - if this_second_comment(): belongs() # to diff --git a/crates/ruff_linter/src/rules/ruff/mod.rs b/crates/ruff_linter/src/rules/ruff/mod.rs index 41f3573126899..3c413d571af9b 100644 --- a/crates/ruff_linter/src/rules/ruff/mod.rs +++ b/crates/ruff_linter/src/rules/ruff/mod.rs @@ -79,6 +79,10 @@ mod tests { #[test_case(Rule::InvalidAssertMessageLiteralArgument, Path::new("RUF040.py"))] #[test_case(Rule::UnnecessaryNestedLiteral, Path::new("RUF041.py"))] #[test_case(Rule::UnnecessaryNestedLiteral, Path::new("RUF041.pyi"))] + #[test_case(Rule::NeedlessElse, Path::new("RUF047_if.py"))] + #[test_case(Rule::NeedlessElse, Path::new("RUF047_for.py"))] + #[test_case(Rule::NeedlessElse, Path::new("RUF047_while.py"))] + #[test_case(Rule::NeedlessElse, Path::new("RUF047_try.py"))] #[test_case(Rule::IfKeyInDictDel, Path::new("RUF051.py"))] #[test_case(Rule::UsedDummyVariable, Path::new("RUF052.py"))] #[test_case(Rule::FalsyDictGetFallback, Path::new("RUF056.py"))] @@ -423,10 +427,6 @@ mod tests { #[test_case(Rule::UnnecessaryRound, Path::new("RUF057.py"))] #[test_case(Rule::DataclassEnum, Path::new("RUF049.py"))] #[test_case(Rule::StarmapZip, Path::new("RUF058.py"))] - #[test_case(Rule::NeedlessElse, Path::new("RUF047_if.py"))] - #[test_case(Rule::NeedlessElse, Path::new("RUF047_for.py"))] - #[test_case(Rule::NeedlessElse, Path::new("RUF047_while.py"))] - #[test_case(Rule::NeedlessElse, Path::new("RUF047_try.py"))] fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!( "preview__{}_{}", diff --git a/crates/ruff_linter/src/rules/ruff/rules/needless_else.rs b/crates/ruff_linter/src/rules/ruff/rules/needless_else.rs index ddf8a293075b3..1989e1de5363f 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/needless_else.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/needless_else.rs @@ -59,7 +59,7 @@ pub(crate) fn needless_else(checker: &mut Checker, stmt: AnyNodeWithOrElse) { return; }; - if else_branch_should_not_be_reported(stmt, else_range, checker) { + if else_contains_comments(stmt, else_range, checker) { return; } @@ -84,36 +84,47 @@ fn body_is_no_op(body: &[Stmt]) -> bool { } } -fn else_branch_should_not_be_reported( +fn else_contains_comments( stmt: AnyNodeWithOrElse, else_range: TextRange, checker: &Checker, ) -> bool { - let Some(preceding_stmt) = stmt.body_before_else().last() else { - return true; - }; - let Some(else_last_stmt) = stmt.else_body().last() else { - return true; - }; - - if else_branch_has_preceding_comment(preceding_stmt, else_range, checker) { - return true; - } - let else_full_end = checker.source().full_line_end(else_range.end()); let commentable_range = TextRange::new(else_range.start(), else_full_end); + // A comment after the `else` keyword or after the dummy statement. + // + // ```python + // if ...: + // ... + // else: # comment + // pass # comment + // ``` if checker.comment_ranges().intersects(commentable_range) { return true; } - if else_branch_has_trailing_comment(else_last_stmt, else_full_end, checker) { - return true; - } + let Some(preceding_stmt) = stmt.body_before_else().last() else { + return false; + }; - false + let Some(else_last_stmt) = stmt.else_body().last() else { + return false; + }; + + else_branch_has_preceding_comment(preceding_stmt, else_range, checker) + || else_branch_has_trailing_comment(else_last_stmt, else_full_end, checker) } +/// Returns `true` if the `else` clause header has a leading own-line comment. +/// +/// ```python +/// if ...: +/// ... +/// # some comment +/// else: +/// pass +/// ``` fn else_branch_has_preceding_comment( preceding_stmt: &Stmt, else_range: TextRange, @@ -122,13 +133,12 @@ fn else_branch_has_preceding_comment( let (tokens, source) = (checker.tokens(), checker.source()); let before_else_full_end = source.full_line_end(preceding_stmt.end()); - let else_start = else_range.start(); let preceding_indentation = indentation(source, &preceding_stmt) .unwrap_or_default() .text_len(); - for token in tokens.in_range(TextRange::new(before_else_full_end, else_start)) { + for token in tokens.in_range(TextRange::new(before_else_full_end, else_range.start())) { if token.kind() != TokenKind::Comment { continue; } @@ -137,8 +147,8 @@ fn else_branch_has_preceding_comment( comment_indentation_after(preceding_stmt.into(), token.range(), source); match comment_indentation.cmp(&preceding_indentation) { - Ordering::Greater => continue, - Ordering::Equal => return true, + // Comment belongs to preceding statement. + Ordering::Greater | Ordering::Equal => continue, Ordering::Less => return true, } } @@ -146,14 +156,23 @@ fn else_branch_has_preceding_comment( false } +/// Returns `true` if the `else` branch has a trailing own line comment: +/// +/// ```python +/// if ...: +/// ... +/// else: +/// pass +/// # some comment +/// ``` fn else_branch_has_trailing_comment( - preceding_stmt: &Stmt, + last_else_stmt: &Stmt, else_full_end: TextSize, checker: &Checker, ) -> bool { let (tokens, source) = (checker.tokens(), checker.source()); - let preceding_indentation = indentation(source, &preceding_stmt) + let preceding_indentation = indentation(source, &last_else_stmt) .unwrap_or_default() .text_len(); @@ -161,11 +180,10 @@ fn else_branch_has_trailing_comment( match token.kind() { TokenKind::Comment => { let comment_indentation = - comment_indentation_after(preceding_stmt.into(), token.range(), source); + comment_indentation_after(last_else_stmt.into(), token.range(), source); match comment_indentation.cmp(&preceding_indentation) { - Ordering::Greater => return true, - Ordering::Equal => return true, + Ordering::Greater | Ordering::Equal => return true, Ordering::Less => break, } } diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_for.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_for.py.snap similarity index 100% rename from crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_for.py.snap rename to crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_for.py.snap diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_if.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_if.py.snap similarity index 58% rename from crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_if.py.snap rename to crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_if.py.snap index 24428045bda82..b8c3a557357ec 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_if.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_if.py.snap @@ -64,74 +64,96 @@ RUF047_if.py:19:1: RUF047 [*] Empty `else` clause RUF047_if.py:26:1: RUF047 [*] Empty `else` clause | 24 | belongs() # to -25 | # `if` +25 | # `if` 26 | / else: 27 | | pass | |________^ RUF047 +28 | +29 | if this_second_comment(): | = help: Remove `else` clause ℹ Safe fix 23 23 | if this_second_comment(): 24 24 | belongs() # to -25 25 | # `if` +25 25 | # `if` 26 |-else: 27 |- pass 28 26 | -29 27 | -30 28 | if of_course(): +29 27 | if this_second_comment(): +30 28 | belongs() # to -RUF047_if.py:38:1: RUF047 [*] Empty `else` clause +RUF047_if.py:32:1: RUF047 [*] Empty `else` clause | -37 | if of_course: this() -38 | else: ... - | ^^^^^^^^^ RUF047 +30 | belongs() # to +31 | # `if` +32 | / else: +33 | | pass + | |________^ RUF047 | = help: Remove `else` clause ℹ Safe fix -35 35 | -36 36 | -37 37 | if of_course: this() -38 |-else: ... -39 38 | -40 39 | -41 40 | if of_course: +29 29 | if this_second_comment(): +30 30 | belongs() # to +31 31 | # `if` +32 |-else: +33 |- pass +34 32 | +35 33 | +36 34 | if of_course(): -RUF047_if.py:43:1: RUF047 [*] Empty `else` clause +RUF047_if.py:44:1: RUF047 [*] Empty `else` clause | -41 | if of_course: -42 | this() # comment -43 | else: ... +43 | if of_course: this() +44 | else: ... | ^^^^^^^^^ RUF047 | = help: Remove `else` clause ℹ Safe fix -40 40 | -41 41 | if of_course: -42 42 | this() # comment -43 |-else: ... -44 43 | +41 41 | +42 42 | +43 43 | if of_course: this() +44 |-else: ... 45 44 | -46 45 | def nested(): +46 45 | +47 46 | if of_course: + +RUF047_if.py:49:1: RUF047 [*] Empty `else` clause + | +47 | if of_course: +48 | this() # comment +49 | else: ... + | ^^^^^^^^^ RUF047 + | + = help: Remove `else` clause + +ℹ Safe fix +46 46 | +47 47 | if of_course: +48 48 | this() # comment +49 |-else: ... +50 49 | +51 50 | +52 51 | def nested(): -RUF047_if.py:49:5: RUF047 [*] Empty `else` clause +RUF047_if.py:55:5: RUF047 [*] Empty `else` clause | -47 | if a: -48 | b() -49 | / else: -50 | | ... +53 | if a: +54 | b() +55 | / else: +56 | | ... | |___________^ RUF047 | = help: Remove `else` clause ℹ Safe fix -46 46 | def nested(): -47 47 | if a: -48 48 | b() -49 |- else: -50 |- ... -51 49 | -52 50 | -53 51 | ### No errors +52 52 | def nested(): +53 53 | if a: +54 54 | b() +55 |- else: +56 |- ... +57 55 | +58 56 | +59 57 | ### No errors diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_try.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_try.py.snap similarity index 100% rename from crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_try.py.snap rename to crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_try.py.snap diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_while.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_while.py.snap similarity index 100% rename from crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF047_RUF047_while.py.snap rename to crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_while.py.snap From 86d26297d4192fd1951d6337a6f79b8442b2fdd6 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Tue, 21 Jan 2025 09:16:13 +0100 Subject: [PATCH 5/5] Change fix title --- .../src/rules/ruff/rules/needless_else.rs | 2 +- ...rules__ruff__tests__RUF047_RUF047_for.py.snap | 4 ++-- ..._rules__ruff__tests__RUF047_RUF047_if.py.snap | 16 ++++++++-------- ...rules__ruff__tests__RUF047_RUF047_try.py.snap | 4 ++-- ...les__ruff__tests__RUF047_RUF047_while.py.snap | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/crates/ruff_linter/src/rules/ruff/rules/needless_else.rs b/crates/ruff_linter/src/rules/ruff/rules/needless_else.rs index 1989e1de5363f..b9db80f3bfe0f 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/needless_else.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/needless_else.rs @@ -40,7 +40,7 @@ impl AlwaysFixableViolation for NeedlessElse { } fn fix_title(&self) -> String { - "Remove `else` clause".to_string() + "Remove the `else` clause".to_string() } } diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_for.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_for.py.snap index e9b392c6468dd..3a0d9bc7a97a7 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_for.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_for.py.snap @@ -9,7 +9,7 @@ RUF047_for.py:6:1: RUF047 [*] Empty `else` clause 7 | | pass | |________^ RUF047 | - = help: Remove `else` clause + = help: Remove the `else` clause ℹ Safe fix 3 3 | for _ in range(0): @@ -29,7 +29,7 @@ RUF047_for.py:12:1: RUF047 [*] Empty `else` clause 13 | | ... | |_______^ RUF047 | - = help: Remove `else` clause + = help: Remove the `else` clause ℹ Safe fix 9 9 | diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_if.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_if.py.snap index b8c3a557357ec..b84cff6ad2572 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_if.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_if.py.snap @@ -9,7 +9,7 @@ RUF047_if.py:5:1: RUF047 [*] Empty `else` clause 6 | | pass | |________^ RUF047 | - = help: Remove `else` clause + = help: Remove the `else` clause ℹ Safe fix 2 2 | @@ -29,7 +29,7 @@ RUF047_if.py:11:1: RUF047 [*] Empty `else` clause 12 | | ... | |_______^ RUF047 | - = help: Remove `else` clause + = help: Remove the `else` clause ℹ Safe fix 8 8 | @@ -49,7 +49,7 @@ RUF047_if.py:19:1: RUF047 [*] Empty `else` clause 20 | | pass | |________^ RUF047 | - = help: Remove `else` clause + = help: Remove the `else` clause ℹ Safe fix 16 16 | treated() @@ -71,7 +71,7 @@ RUF047_if.py:26:1: RUF047 [*] Empty `else` clause 28 | 29 | if this_second_comment(): | - = help: Remove `else` clause + = help: Remove the `else` clause ℹ Safe fix 23 23 | if this_second_comment(): @@ -91,7 +91,7 @@ RUF047_if.py:32:1: RUF047 [*] Empty `else` clause 33 | | pass | |________^ RUF047 | - = help: Remove `else` clause + = help: Remove the `else` clause ℹ Safe fix 29 29 | if this_second_comment(): @@ -109,7 +109,7 @@ RUF047_if.py:44:1: RUF047 [*] Empty `else` clause 44 | else: ... | ^^^^^^^^^ RUF047 | - = help: Remove `else` clause + = help: Remove the `else` clause ℹ Safe fix 41 41 | @@ -127,7 +127,7 @@ RUF047_if.py:49:1: RUF047 [*] Empty `else` clause 49 | else: ... | ^^^^^^^^^ RUF047 | - = help: Remove `else` clause + = help: Remove the `else` clause ℹ Safe fix 46 46 | @@ -146,7 +146,7 @@ RUF047_if.py:55:5: RUF047 [*] Empty `else` clause 56 | | ... | |___________^ RUF047 | - = help: Remove `else` clause + = help: Remove the `else` clause ℹ Safe fix 52 52 | def nested(): diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_try.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_try.py.snap index 3a76ee2750bb6..e27078492cd2c 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_try.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_try.py.snap @@ -9,7 +9,7 @@ RUF047_try.py:7:1: RUF047 [*] Empty `else` clause 8 | | pass | |________^ RUF047 | - = help: Remove `else` clause + = help: Remove the `else` clause ℹ Safe fix 4 4 | raise try_body_is_not_checked() @@ -29,7 +29,7 @@ RUF047_try.py:17:1: RUF047 [*] Empty `else` clause 18 | | ... | |_______^ RUF047 | - = help: Remove `else` clause + = help: Remove the `else` clause ℹ Safe fix 14 14 | belongs() diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_while.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_while.py.snap index f1f515bf19dbc..c149edd4edd06 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_while.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF047_RUF047_while.py.snap @@ -9,7 +9,7 @@ RUF047_while.py:6:1: RUF047 [*] Empty `else` clause 7 | | pass | |________^ RUF047 | - = help: Remove `else` clause + = help: Remove the `else` clause ℹ Safe fix 3 3 | while True: @@ -29,7 +29,7 @@ RUF047_while.py:12:1: RUF047 [*] Empty `else` clause 13 | | ... | |_______^ RUF047 | - = help: Remove `else` clause + = help: Remove the `else` clause ℹ Safe fix 9 9 |