Don't trigger `eq-without-hash` when `__hash__` is explicitly set to `None` (#6739)

This commit is contained in:
Victor Hugo Gomes 2023-08-21 20:51:21 -03:00 committed by GitHub
parent c0df99b965
commit 37f4920e1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 34 additions and 10 deletions

View File

@ -1,10 +1,11 @@
class Person:
class Person: # [eq-without-hash]
def __init__(self):
self.name = "monty"
def __eq__(self, other):
return isinstance(other, Person) and other.name == self.name
# OK
class Language:
def __init__(self):
self.name = "python"
@ -14,3 +15,9 @@ class Language:
def __hash__(self):
return hash(self.name)
class MyClass:
def __eq__(self, other):
return True
__hash__ = None

View File

@ -1,7 +1,7 @@
use ruff_python_ast::{self as ast, Ranged, Stmt};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::is_const_none;
use ruff_python_ast::{self as ast, Expr, Ranged, Stmt};
use crate::checkers::ast::Checker;
@ -65,12 +65,29 @@ fn has_eq_without_hash(body: &[Stmt]) -> bool {
let mut has_hash = false;
let mut has_eq = false;
for statement in body {
let Stmt::FunctionDef(ast::StmtFunctionDef { name, .. }) = statement else {
continue;
};
match name.as_str() {
"__hash__" => has_hash = true,
"__eq__" => has_eq = true,
match statement {
Stmt::Assign(ast::StmtAssign { targets, value, .. }) => {
let [Expr::Name(ast::ExprName { id, .. })] = targets.as_slice() else {
continue;
};
// Check if `__hash__` was explicitly set to `None`, as in:
// ```python
// class Class:
// def __eq__(self, other):
// return True
//
// __hash__ = None
// ```
if id == "__hash__" && is_const_none(value) {
has_hash = true;
}
}
Stmt::FunctionDef(ast::StmtFunctionDef { name, .. }) => match name.as_str() {
"__hash__" => has_hash = true,
"__eq__" => has_eq = true,
_ => {}
},
_ => {}
}
}

View File

@ -3,7 +3,7 @@ source: crates/ruff/src/rules/pylint/mod.rs
---
eq_without_hash.py:1:7: PLW1641 Object does not implement `__hash__` method
|
1 | class Person:
1 | class Person: # [eq-without-hash]
| ^^^^^^ PLW1641
2 | def __init__(self):
3 | self.name = "monty"