|
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