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`. */
1716class 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+
4959from EqualsMethod m
5060where
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
8776select m , "equals() method does not seem to check argument type."
0 commit comments