Skip to content

Commit df0095b

Browse files
committed
Extend equals-hashCode-calling-Object-super.ql to cover more cases
1 parent 8f49eb6 commit df0095b

File tree

1 file changed

+46
-28
lines changed

1 file changed

+46
-28
lines changed
Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,62 @@
11
/**
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`.
46
* 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.
68
*
79
* If the intention is to check `obj == this` by calling `Object.equals`,
810
* then it is better to write that explicitly. Otherwise one might wonder
911
* (without checking which parent classes a class has) why the `super.equals`
1012
* check is not at the beginning of the method and why the method does not
1113
* 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
1235
*/
1336

1437
import java
1538

1639
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
2241
}
2342

24-
from Method method, MethodAccess superCall
43+
from Method enclosingMethod, MethodAccess superCall, Method calledMethod
2544
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

Comments
 (0)