|
| 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