|
1 | 1 | /** |
2 | | - * Finds classes which only extend Object, but call `super.equals`, |
3 | | - * respectively `super.hashCode` in their `equals` / `hashCode` implementation. |
| 2 | + * Finds classes which call `super.equals` or `super.hashCode` in their |
| 3 | + * `equals` / `hashCode` implementation, but for which no superclass |
| 4 | + * overrides these methods, therefore effectively calling `Object.equals` or |
| 5 | + * `Object.hashCode`. |
4 | 6 | * This defeats the purpose of implementing own equality criteria, because |
5 | | - * the implementation by Object only checks for identity. |
| 7 | + * the implementation by `Object` only checks for identity. |
6 | 8 | * |
7 | 9 | * If the intention is to check `obj == this` by calling `Object.equals`, |
8 | 10 | * then it is better to write that explicitly. Otherwise one might wonder |
9 | 11 | * (without checking which parent classes a class has) why the `super.equals` |
10 | 12 | * check is not at the beginning of the method and why the method does not |
11 | 13 | * return fast if the result is `false`. |
| 14 | + * |
| 15 | + * For example: |
| 16 | + * ```java |
| 17 | + * class MyClass { |
| 18 | + * int i; |
| 19 | + * |
| 20 | + * @Override |
| 21 | + * public boolean equals(Object o) { |
| 22 | + * if (!(o instanceof MyClass)) { |
| 23 | + * return false; |
| 24 | + * } |
| 25 | + * |
| 26 | + * MyClass other = (MyClass) o; |
| 27 | + * // Bug: super.equals call here is wrong; will prevent two separate |
| 28 | + * // instances with same data from being considered equal |
| 29 | + * return super.equals(other) && i == other.i; |
| 30 | + * } |
| 31 | + * } |
| 32 | + * ``` |
| 33 | + * |
| 34 | + * @kind problem |
12 | 35 | */ |
13 | 36 |
|
14 | 37 | import java |
15 | 38 |
|
16 | 39 | predicate delegatesToParent(Method m, MethodAccess superCall) { |
17 | | - m.getBody().getNumStmt() = 1 |
18 | | - and exists (ReturnStmt returnStmt | |
19 | | - m.getBody().getLastStmt() = returnStmt |
20 | | - and returnStmt.getResult() = superCall |
21 | | - ) |
| 40 | + m.getBody().(SingletonBlock).getStmt().(ReturnStmt).getResult() = superCall |
22 | 41 | } |
23 | 42 |
|
24 | | -from Method method, MethodAccess superCall |
| 43 | +from Method enclosingMethod, MethodAccess superCall, Method calledMethod |
25 | 44 | where |
26 | | - // Verify that class has Object as its super class |
27 | | - exists (TypeObject object | method.getDeclaringType().hasSupertype(object)) |
28 | | - and superCall.getQualifier() instanceof SuperAccess |
29 | | - and superCall.getEnclosingCallable() = method |
30 | | - and ( |
31 | | - ( |
32 | | - method instanceof EqualsMethod |
33 | | - and superCall.getMethod() instanceof EqualsMethod |
34 | | - ) |
35 | | - or |
36 | | - ( |
37 | | - method instanceof HashCodeMethod |
38 | | - and superCall.getMethod() instanceof HashCodeMethod |
39 | | - ) |
40 | | - ) |
41 | | - // Ignore implementations which simply delegate to the parent |
42 | | - // E.g. to change the method javadoc |
43 | | - and not delegatesToParent(method, superCall) |
44 | | -select method, superCall |
| 45 | + superCall.getQualifier() instanceof SuperAccess and |
| 46 | + superCall.getMethod() = calledMethod and |
| 47 | + // Called method is not overridden; directly calls Object method |
| 48 | + calledMethod.getDeclaringType() instanceof TypeObject and |
| 49 | + superCall.getEnclosingCallable() = enclosingMethod and |
| 50 | + // Check that calls occur in same method, e.g. `equals` call in `equals` method, |
| 51 | + // otherwise call might be intentional |
| 52 | + ( |
| 53 | + enclosingMethod instanceof EqualsMethod and |
| 54 | + calledMethod instanceof EqualsMethod |
| 55 | + or |
| 56 | + enclosingMethod instanceof HashCodeMethod and |
| 57 | + calledMethod instanceof HashCodeMethod |
| 58 | + ) and |
| 59 | + // Ignore implementations which simply delegate to the parent |
| 60 | + // E.g. to change the method javadoc |
| 61 | + not delegatesToParent(enclosingMethod, superCall) |
| 62 | +select superCall, "Calls `Object." + calledMethod.getName() + "`" |
0 commit comments