-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathEqualsOrHash.ql
More file actions
46 lines (40 loc) · 1.65 KB
/
EqualsOrHash.ql
File metadata and controls
46 lines (40 loc) · 1.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
* @name Inconsistent equality and hashing
* @description Defining equality for a class without also defining hashability (or vice-versa) violates the object model.
* @kind problem
* @tags reliability
* correctness
* external/cwe/cwe-581
* @problem.severity warning
* @sub-severity high
* @precision very-high
* @id py/equals-hash-mismatch
*/
import python
FunctionObject defines_equality(ClassObject c, string name) {
(name = "__eq__" or major_version() = 2 and name = "__cmp__")
and
result = c.declaredAttribute(name)
}
FunctionObject implemented_method(ClassObject c, string name) {
result = defines_equality(c, name)
or
result = c.declaredAttribute("__hash__") and name = "__hash__"
}
string unimplemented_method(ClassObject c) {
not exists(defines_equality(c, _)) and
(result = "__eq__" and major_version() = 3 or major_version() = 2 and result = "__eq__ or __cmp__")
or
/* Python 3 automatically makes classes unhashable if __eq__ is defined, but __hash__ is not */
not c.declaresAttribute(result) and result = "__hash__" and major_version() = 2
}
predicate violates_hash_contract(ClassObject c, string present, string missing, Object method) {
not c.unhashable() and
missing = unimplemented_method(c) and
method = implemented_method(c, present) and
not c.unknowableAttributes()
}
from ClassObject c, string present, string missing, FunctionObject method
where violates_hash_contract(c, present, missing, method) and
exists(c.getPyClass()) // Suppress results that aren't from source
select method, "Class $@ implements " + present + " but does not define " + missing + ".", c, c.getName()