-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathIncomparableEquals.ql
More file actions
65 lines (58 loc) · 2.25 KB
/
IncomparableEquals.ql
File metadata and controls
65 lines (58 loc) · 2.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**
* @name Equals on incomparable types
* @description Calls of the form 'x.equals(y)', where the types of 'x' and 'y' are incomparable,
* should always return 'false'.
* @kind problem
* @problem.severity error
* @precision very-high
* @id java/equals-on-unrelated-types
* @tags quality
* reliability
* correctness
* external/cwe/cwe-571
*/
import java
/** A call to an `equals` method. */
class EqualsCall extends MethodCall {
EqualsCall() { this.getMethod() instanceof EqualsMethod }
/**
* A whitelist of method accesses allowed to perform
* an incomparable-equals call.
*/
predicate whitelisted() {
// Allow tests and assertions to verify that `equals` methods return `false`.
this.getParent*().(MethodCall).getMethod().getName().matches("assert%") or
this.getEnclosingStmt() instanceof AssertStmt
}
/** Holds if the callee of this method access is `Object.equals`. */
predicate invokesObjectEquals() { this.getMethod().getDeclaringType() instanceof TypeObject }
/** Return the (static) type of the argument to `equals`. */
RefType getArgumentType() { result = this.getArgument(0).getType() }
}
/*
* Find calls to `equals` where the receiver and argument types are incomparable.
*
* We check this in different ways, depending on whether the call invokes
* the trivial `equals` implementation in `Object` or not.
*
* If it does, the two operands have to be of the same runtime type, so if their
* static types have no intersection, the result is guaranteed to be false.
*
* If the `equals` method being invoked is not `Object.equals` but some overridden
* version `A.equals`, we assume that `A.equals` requires the runtime type of its
* operand to be a subtype of `A`. Hence, if `A` and the static type of the argument
* have no intersection, the result is again guaranteed to be false.
*/
from EqualsCall ma, RefType recvtp, RefType argtp
where
not ma.whitelisted() and
(
if ma.invokesObjectEquals()
then recvtp = ma.getReceiverType()
else recvtp = ma.getMethod().getDeclaringType()
) and
argtp = ma.getArgumentType() and
notHaveIntersection(recvtp, argtp)
select ma,
"Call to equals() comparing incomparable types " + recvtp.getName() + " and " + argtp.getName() +
"."