-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathMissingInstanceofInEquals.ql
More file actions
82 lines (76 loc) · 2.46 KB
/
MissingInstanceofInEquals.ql
File metadata and controls
82 lines (76 loc) · 2.46 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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/**
* @name Equals method does not inspect argument type
* @description An implementation of 'equals' that does not check the type
* of its argument may lead to failing casts.
* @kind problem
* @problem.severity error
* @precision high
* @id java/unchecked-cast-in-equals
* @tags reliability
* correctness
*/
import java
/** A cast inside a try-catch block that catches `ClassCastException`. */
class CheckedCast extends CastExpr {
CheckedCast() {
exists(TryStmt try, RefType cce |
this.getEnclosingStmt().getEnclosingStmt+() = try and
try.getACatchClause().getVariable().getType() = cce and
cce.getQualifiedName() = "java.lang.ClassCastException"
)
}
}
predicate hasTypeTest(Variable v) {
any(InstanceOfExpr ioe).getExpr() = v.getAnAccess()
or
any(NotInstanceOfExpr nioe).getExpr() = v.getAnAccess()
or
any(SafeCastExpr sce).getExpr() = v.getAnAccess()
or
any(ClassExpr c).getExpr() = v.getAnAccess()
or
exists(MethodAccess ma |
ma.getMethod().getName() = "getClass" and
ma.getQualifier() = v.getAnAccess()
)
or
any(CheckedCast cc).getExpr() = v.getAnAccess()
or
exists(Parameter p | hasTypeTest(p) and p.getAnArgument() = v.getAnAccess())
}
/**
* An `equals` method with a body of either `return o == this;`
* or `return o == field;`
*/
class ReferenceEquals extends EqualsMethod {
ReferenceEquals() {
exists(BlockStmt b, ReturnStmt ret, EQExpr eq |
this.getBody() = b and
b.getStmt(0) = ret and
ret.getResult() = eq and
eq.getAnOperand() = this.getAParameter().getAnAccess() and
(eq.getAnOperand() instanceof ThisAccess or eq.getAnOperand() instanceof FieldAccess)
)
}
}
class UnimplementedEquals extends EqualsMethod {
UnimplementedEquals() { this.getBody().getStmt(0) instanceof ThrowStmt }
}
from EqualsMethod m
where
exists(m.getBody()) and
exists(Parameter p | p = m.getAParameter() |
// The parameter has no type test
not hasTypeTest(p) and
// If the parameter is passed to a method for which we don't have the source
// we assume it's ok
not exists(MethodAccess ma |
not exists(ma.getMethod().getBody()) and
ma.getAnArgument() = p.getAnAccess()
)
) and
not m.getDeclaringType() instanceof Interface and
// Exclude `equals` methods that implement reference-equality.
not m instanceof ReferenceEquals and
not m instanceof UnimplementedEquals
select m, "This 'equals()' method does not check argument type."