diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/invalid_return_type_hash.py b/crates/ruff_linter/resources/test/fixtures/pylint/invalid_return_type_hash.py index 166053833ed25..cd43330f2c887 100644 --- a/crates/ruff_linter/resources/test/fixtures/pylint/invalid_return_type_hash.py +++ b/crates/ruff_linter/resources/test/fixtures/pylint/invalid_return_type_hash.py @@ -21,12 +21,6 @@ def __hash__(self): print("ruff") # [invalid-hash-return] -class HashWrongRaise: - def __hash__(self): - print("raise some error") - raise NotImplementedError # [invalid-hash-return] - - # TODO: Once Ruff has better type checking def return_int(): return "3" @@ -37,7 +31,6 @@ def __hash__(self): return return_int() # [invalid-hash-return] - # These testcases should NOT raise errors @@ -64,3 +57,9 @@ def __hash__(self): class Hash5: def __hash__(self): raise NotImplementedError + + +class HashWrong6: + def __hash__(self): + print("raise some error") + raise NotImplementedError diff --git a/crates/ruff_linter/src/rules/pylint/rules/invalid_hash_return.rs b/crates/ruff_linter/src/rules/pylint/rules/invalid_hash_return.rs index 568cc88fb97ac..fe585ccf5d287 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/invalid_hash_return.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/invalid_hash_return.rs @@ -5,6 +5,7 @@ use ruff_python_ast::identifier::Identifier; use ruff_python_ast::visitor::Visitor; use ruff_python_ast::{self as ast}; use ruff_python_semantic::analyze::function_type::is_stub; +use ruff_python_semantic::analyze::terminal::Terminal; use ruff_python_semantic::analyze::type_inference::{NumberLike, PythonType, ResolvedPythonType}; use ruff_text_size::Ranged; @@ -44,7 +45,7 @@ pub struct InvalidHashReturnType; impl Violation for InvalidHashReturnType { #[derive_message_formats] fn message(&self) -> String { - format!("`__hash__` does not return `integer`") + format!("`__hash__` does not return an integer") } } @@ -62,19 +63,29 @@ pub(crate) fn invalid_hash_return(checker: &mut Checker, function_def: &ast::Stm return; } - let returns = { - let mut visitor = ReturnStatementVisitor::default(); - visitor.visit_body(&function_def.body); - visitor.returns - }; + // Determine the terminal behavior (i.e., implicit return, no return, etc.). + let terminal = Terminal::from_function(function_def); + + // If every control flow path raises an exception, ignore the function. + if terminal == Terminal::Raise { + return; + } - if returns.is_empty() { + // If there are no return statements, add a diagnostic. + if terminal == Terminal::Implicit { checker.diagnostics.push(Diagnostic::new( InvalidHashReturnType, function_def.identifier(), )); + return; } + let returns = { + let mut visitor = ReturnStatementVisitor::default(); + visitor.visit_body(&function_def.body); + visitor.returns + }; + for stmt in returns { if let Some(value) = stmt.value.as_deref() { if !matches!( diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE0309_invalid_return_type_hash.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE0309_invalid_return_type_hash.py.snap index 209b82ae60293..cffa2bcfc47d9 100644 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE0309_invalid_return_type_hash.py.snap +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE0309_invalid_return_type_hash.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pylint/mod.rs --- -invalid_return_type_hash.py:6:16: PLE0309 `__hash__` does not return `integer` +invalid_return_type_hash.py:6:16: PLE0309 `__hash__` does not return an integer | 4 | class Bool: 5 | def __hash__(self): @@ -9,7 +9,7 @@ invalid_return_type_hash.py:6:16: PLE0309 `__hash__` does not return `integer` | ^^^^ PLE0309 | -invalid_return_type_hash.py:11:16: PLE0309 `__hash__` does not return `integer` +invalid_return_type_hash.py:11:16: PLE0309 `__hash__` does not return an integer | 9 | class Float: 10 | def __hash__(self): @@ -17,7 +17,7 @@ invalid_return_type_hash.py:11:16: PLE0309 `__hash__` does not return `integer` | ^^^^ PLE0309 | -invalid_return_type_hash.py:16:16: PLE0309 `__hash__` does not return `integer` +invalid_return_type_hash.py:16:16: PLE0309 `__hash__` does not return an integer | 14 | class Str: 15 | def __hash__(self): @@ -25,19 +25,10 @@ invalid_return_type_hash.py:16:16: PLE0309 `__hash__` does not return `integer` | ^^^^^^ PLE0309 | -invalid_return_type_hash.py:20:9: PLE0309 `__hash__` does not return `integer` +invalid_return_type_hash.py:20:9: PLE0309 `__hash__` does not return an integer | 19 | class HashNoReturn: 20 | def __hash__(self): | ^^^^^^^^ PLE0309 21 | print("ruff") # [invalid-hash-return] | - -invalid_return_type_hash.py:25:9: PLE0309 `__hash__` does not return `integer` - | -24 | class HashWrongRaise: -25 | def __hash__(self): - | ^^^^^^^^ PLE0309 -26 | print("raise some error") -27 | raise NotImplementedError # [invalid-hash-return] - |