@@ -16,7 +16,7 @@ import python
1616 * For sequences, the index must be an int, which are hashable, so we don't need to treat them specially.
1717 * For numpy arrays, the index may be a list, which are not hashable and needs to be treated specially.
1818 */
19-
19+
2020predicate numpy_array_type ( ClassObject na ) {
2121 exists ( ModuleObject np | np .getName ( ) = "numpy" or np .getName ( ) = "numpy.core" |
2222 na .getAnImproperSuperType ( ) = np .getAttribute ( "ndarray" )
@@ -51,9 +51,30 @@ predicate is_unhashable(ControlFlowNode f, ClassObject cls, ControlFlowNode orig
5151 )
5252}
5353
54+ /**
55+ * Holds if `f` is inside a `try` that catches `TypeError`. For example:
56+ *
57+ * try:
58+ * ... f ...
59+ * except TypeError:
60+ * ...
61+ *
62+ * This predicate is used to eliminate false positive results. If `hash`
63+ * is called on an unhashable object then a `TypeError` will be thrown.
64+ * But this is not a bug if the code catches the `TypeError` and handles
65+ * it.
66+ */
67+ predicate typeerror_is_caught ( ControlFlowNode f ) {
68+ exists ( Try try |
69+ try .getBody ( ) .contains ( f .getNode ( ) ) and
70+ try .getAHandler ( ) .getType ( ) .refersTo ( theTypeErrorType ( ) ) )
71+ }
72+
5473from ControlFlowNode f , ClassObject c , ControlFlowNode origin
55- where
56- explicitly_hashed ( f ) and is_unhashable ( f , c , origin )
57- or
58- unhashable_subscript ( f , c , origin )
74+ where
75+ not typeerror_is_caught ( f )
76+ and
77+ ( explicitly_hashed ( f ) and is_unhashable ( f , c , origin )
78+ or
79+ unhashable_subscript ( f , c , origin ) )
5980select f .getNode ( ) , "This $@ of $@ is unhashable." , origin , "instance" , c , c .getQualifiedName ( )
0 commit comments