Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c199f2e

Browse files
committed
Java: Improve precision of MissingInstanceofInEquals.ql
1 parent a25acd0 commit c199f2e

2 files changed

Lines changed: 39 additions & 40 deletions

File tree

change-notes/1.22/analysis-java.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Improvements to Java analysis
2+
3+
## Changes to existing queries
4+
5+
| **Query** | **Expected impact** | **Change** |
6+
|----------------------------|------------------------|------------------------------------------------------------------|
7+
| Equals method does not inspect argument type (`java/unchecked-cast-in-equals`) | Fewer false positive and more true positive results | Precision has been improved by doing a bit of inter-procedural analysis and relying less on ad-hoc method names. |
8+
9+
## Changes to QL libraries
10+

java/ql/src/Likely Bugs/Comparison/MissingInstanceofInEquals.ql

Lines changed: 29 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
* correctness
1111
*/
1212

13-
import semmle.code.java.Member
14-
import semmle.code.java.JDK
13+
import java
1514

1615
/** A cast inside a try-catch block that catches `ClassCastException`. */
1716
class CheckedCast extends CastExpr {
@@ -22,8 +21,19 @@ class CheckedCast extends CastExpr {
2221
cce.getQualifiedName() = "java.lang.ClassCastException"
2322
)
2423
}
24+
}
2525

26-
Variable castVariable() { result.getAnAccess() = this.getExpr() }
26+
predicate hasTypeTest(Variable v) {
27+
any(InstanceOfExpr ioe).getExpr() = v.getAnAccess()
28+
or
29+
exists(MethodAccess ma |
30+
ma.getMethod().getName() = "getClass" and
31+
ma.getQualifier() = v.getAnAccess()
32+
)
33+
or
34+
any(CheckedCast cc).getExpr() = v.getAnAccess()
35+
or
36+
exists(Parameter p | hasTypeTest(p) and p.getAnArgument() = v.getAnAccess())
2737
}
2838

2939
/**
@@ -35,53 +45,32 @@ class ReferenceEquals extends EqualsMethod {
3545
exists(Block b, ReturnStmt ret, EQExpr eq |
3646
this.getBody() = b and
3747
b.getStmt(0) = ret and
38-
(
39-
ret.getResult() = eq
40-
or
41-
exists(ParExpr pe | ret.getResult() = pe and pe.getExpr() = eq)
42-
) and
48+
ret.getResult().getProperExpr() = eq and
4349
eq.getAnOperand() = this.getAParameter().getAnAccess() and
4450
(eq.getAnOperand() instanceof ThisAccess or eq.getAnOperand() instanceof FieldAccess)
4551
)
4652
}
4753
}
4854

55+
class UnimplementedEquals extends EqualsMethod {
56+
UnimplementedEquals() { this.getBody().getStmt(0) instanceof ThrowStmt }
57+
}
58+
4959
from EqualsMethod m
5060
where
51-
// The parameter is accessed at least once ...
52-
exists(VarAccess va | va.getVariable() = m.getParameter()) and
53-
// ... but its type is not checked using `instanceof`.
54-
not exists(InstanceOfExpr e |
55-
e.getEnclosingCallable() = m and
56-
e.getExpr().(VarAccess).getVariable() = m.getParameter()
57-
) and
58-
// Exclude cases that are probably OK.
59-
not exists(MethodAccess ma, Method c | ma.getEnclosingCallable() = m and ma.getMethod() = c |
60-
c.hasName("getClass")
61-
or
62-
c.hasName("compareTo")
63-
or
64-
c.hasName("equals")
65-
or
66-
c.hasName("isInstance")
67-
or
68-
c.hasName("reflectionEquals")
69-
or
70-
// If both `this` and the argument are passed to another method,
71-
// or if the argument is passed to a method declared or inherited by `this` type,
72-
// that method may do the right thing.
73-
ma.getAnArgument() = m.getParameter().getAnAccess() and
74-
(
75-
ma.getAnArgument() instanceof ThisAccess
76-
or
77-
exists(Method delegate | delegate.getSourceDeclaration() = ma.getMethod() |
78-
m.getDeclaringType().inherits(delegate)
79-
)
61+
exists(m.getBody()) and
62+
exists(Parameter p | p = m.getAParameter() |
63+
// The parameter has no type test
64+
not hasTypeTest(p) and
65+
// If the parameter is passed to a method for which we don't have the source
66+
// we assume it's ok
67+
not exists(MethodAccess ma |
68+
not exists(ma.getMethod().getBody()) and
69+
ma.getAnArgument() = p.getAnAccess()
8070
)
8171
) and
8272
not m.getDeclaringType() instanceof Interface and
83-
// Exclude checked casts (casts inside `try`-blocks).
84-
not exists(CheckedCast cast | cast.castVariable() = m.getAParameter()) and
8573
// Exclude `equals` methods that implement reference-equality.
86-
not m instanceof ReferenceEquals
74+
not m instanceof ReferenceEquals and
75+
not m instanceof UnimplementedEquals
8776
select m, "equals() method does not seem to check argument type."

0 commit comments

Comments
 (0)