Skip to content

Commit 70e7567

Browse files
committed
Add hashCode-lossy-cast.ql
1 parent 4f0ccff commit 70e7567

File tree

1 file changed

+49
-0
lines changed

1 file changed

+49
-0
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* Finds `hashCode` implementations which cast `float`, `double` or `long` values to `int` for the
3+
* hash code calculation. This conversion is lossy for values outside the `int` value range, and
4+
* can therefore increase chances of a hash collision for not-equal values.
5+
*
6+
* Instead prefer the `hashCode` methods of the boxed types, for example `Long#hashCode(long)`,
7+
* to obtain an `int` for further calculations.
8+
*
9+
* @id TODO
10+
* @kind problem
11+
*/
12+
13+
import java
14+
15+
Member getOwnAccessedMember(Expr e) {
16+
exists(FieldRead f | f = e | f.isOwnFieldAccess() and result = f.getField())
17+
or
18+
exists(MethodAccess c | c = e | c.isOwnMethodAccess() and result = c.getMethod())
19+
}
20+
21+
from HashCodeMethod hashCodeMethod, Expr castingExpr, BoxedType boxedSourceType
22+
where
23+
castingExpr.getEnclosingCallable() = hashCodeMethod and
24+
castingExpr.getType().hasName("int") and
25+
boxedSourceType.hasName(["Float", "Double", "Long"]) and
26+
(
27+
exists(Expr castTarget |
28+
castTarget = castingExpr.(CastExpr).getExpr() and
29+
boxedSourceType.getPrimitiveType() = castTarget.getType() and
30+
exists(Member accessedMember |
31+
// Make sure cast expression is directly performed on field value
32+
accessedMember = getOwnAccessedMember(castTarget) and
33+
// and code is not manually performing lossless calculation with bit operations
34+
not exists(BinaryExpr shiftExpr, Expr shifted |
35+
(shiftExpr instanceof RightShiftExpr or shiftExpr instanceof UnsignedRightShiftExpr) and
36+
shiftExpr.getEnclosingCallable() = hashCodeMethod and
37+
shifted = shiftExpr.getLeftOperand()
38+
|
39+
accessedMember = getOwnAccessedMember(shifted)
40+
)
41+
)
42+
)
43+
or
44+
exists(MethodAccess intValueCall | intValueCall = castingExpr |
45+
intValueCall.getMethod().hasName("intValue") and
46+
boxedSourceType = intValueCall.getQualifier().getType()
47+
)
48+
)
49+
select castingExpr, "Should use " + boxedSourceType.getName() + "#hashCode instead"

0 commit comments

Comments
 (0)