Kotlin: Add types for the different kinds of casts that Kotlin has

We might want to unify some of these in future, but doing that
correctly is easier than splitting them up correctly, so I've given each
one its own QL class for now.

I am not familiar with many of the libraries/queries that use CastExpr.
I've briefly looked at them and updated them in a way that looks
superficially reasonable, but some of the uses will probably want to be
refined later.
This commit is contained in:
Ian Lynagh
2022-01-14 16:20:07 +00:00
parent 7cf1289385
commit 6566f7b69f
30 changed files with 155 additions and 96 deletions

View File

@@ -695,11 +695,14 @@ case @expr.kind of
| 75 = @whenexpr
| 76 = @getclassexpr
| 77 = @safecastexpr
| 78 = @notinstanceofexpr
| 79 = @stmtexpr
| 80 = @stringtemplateexpr
| 81 = @varargexpr
| 82 = @notnullexpr
| 78 = @implicitcastexpr
| 79 = @implicitnotnullexpr
| 80 = @implicitcoerciontounitexpr
| 81 = @notinstanceofexpr
| 82 = @stmtexpr
| 83 = @stringtemplateexpr
| 84 = @varargexpr
| 85 = @notnullexpr
;
/** Holds if this `when` expression was written as an `if` expression. */

View File

@@ -456,9 +456,7 @@ private module ControlFlowGraphImpl {
or
this instanceof UnaryExpr and not this instanceof LogNotExpr
or
this instanceof CastExpr
or
this instanceof SafeCastExpr
this instanceof CastingExpr
or
this instanceof InstanceOfExpr and not this.(InstanceOfExpr).isPattern()
or
@@ -544,9 +542,7 @@ private module ControlFlowGraphImpl {
or
index = 0 and result = this.(UnaryExpr).getExpr()
or
index = 0 and result = this.(CastExpr).getExpr()
or
index = 0 and result = this.(SafeCastExpr).getExpr()
index = 0 and result = this.(CastingExpr).getExpr()
or
index = 0 and result = this.(InstanceOfExpr).getExpr()
or

View File

@@ -111,28 +111,16 @@ class StringConversionContext extends ConversionSite {
override string kind() { result = "string context" }
}
class CastConversionContext extends ConversionSite {
CastExpr c;
class CastingConversionContext extends ConversionSite {
CastingExpr c;
CastConversionContext() { this = c.getExpr() }
CastingConversionContext() { this = c.getExpr() }
override Type getConversionTarget() { result = c.getType() }
override predicate isImplicit() { none() }
override string kind() { result = "cast context" }
}
class SafeCastConversionContext extends ConversionSite {
SafeCastExpr c;
SafeCastConversionContext() { this = c.getExpr() }
override Type getConversionTarget() { result = c.getType() }
override predicate isImplicit() { none() }
override string kind() { result = "safe cast context" }
override string kind() { result = "casting context" }
}
/**

View File

@@ -127,7 +127,7 @@ class CompileTimeConstantExpr extends Expr {
this instanceof Literal
or
// Casts to primitive types and casts to type `String`.
this.(CastExpr).getExpr().isCompileTimeConstant()
this.(CastingExpr).getExpr().isCompileTimeConstant()
or
// The unary operators `+`, `-`, `~`, and `!` (but not `++` or `--`).
this.(PlusExpr).getExpr().isCompileTimeConstant()
@@ -311,7 +311,7 @@ class CompileTimeConstantExpr extends Expr {
or
result = this.(CharacterLiteral).getCodePointValue()
or
exists(CastExpr cast, int val |
exists(CastingExpr cast, int val |
cast = this and val = cast.getExpr().(CompileTimeConstantExpr).getIntValue()
|
if cast.getType().hasName("byte")
@@ -1138,35 +1138,62 @@ class LogNotExpr extends UnaryExpr, @lognotexpr {
override string getAPrimaryQlClass() { result = "LogNotExpr" }
}
/** A cast expression. */
class CastExpr extends Expr, @castexpr {
/** Gets the target type of this cast expression. */
class CastingExpr extends Expr {
CastingExpr() {
this instanceof @castexpr or
this instanceof @safecastexpr or
this instanceof @implicitcastexpr or
this instanceof @implicitnotnullexpr or
this instanceof @implicitcoerciontounitexpr
}
/** Gets the target type of this casting expression. */
Expr getTypeExpr() { result.isNthChildOf(this, 0) }
/** Gets the expression to which the cast operator is applied. */
/** Gets the expression to which the casting operator is applied. */
Expr getExpr() { result.isNthChildOf(this, 1) }
}
/** A cast expression. */
class CastExpr extends CastingExpr, @castexpr {
/** Gets a printable representation of this expression. */
override string toString() { result = "(...)..." }
override string getAPrimaryQlClass() { result = "CastExpr" }
}
// TODO: Would this be better as a predicate on CastExpr?
/** A safe cast expression. */
class SafeCastExpr extends Expr, @safecastexpr {
/** Gets the target type of this cast expression. */
Expr getTypeExpr() { result.isNthChildOf(this, 0) }
/** Gets the expression to which the cast operator is applied. */
Expr getExpr() { result.isNthChildOf(this, 1) }
class SafeCastExpr extends CastingExpr, @safecastexpr {
/** Gets a printable representation of this expression. */
override string toString() { result = "... as? ..." }
override string getAPrimaryQlClass() { result = "SafeCastExpr" }
}
/** An implicit cast expression. */
class ImplicitCastExpr extends CastingExpr, @implicitcastexpr {
/** Gets a printable representation of this expression. */
override string toString() { result = "<implicit cast>" }
override string getAPrimaryQlClass() { result = "ImplicitCastExpr" }
}
/** An implicit cast-to-non-null expression. */
class ImplicitNotNullExpr extends CastingExpr, @implicitnotnullexpr {
/** Gets a printable representation of this expression. */
override string toString() { result = "<implicit not null>" }
override string getAPrimaryQlClass() { result = "ImplicitNotNullExpr" }
}
/** An implicit coercion-to-unit expression. */
class ImplicitCoercionToUnitExpr extends CastingExpr, @implicitcoerciontounitexpr {
/** Gets a printable representation of this expression. */
override string toString() { result = "<implicit coercion to unit>" }
override string getAPrimaryQlClass() { result = "ImplicitCoercionToUnitExpr" }
}
/** A class instance creation expression. */
class ClassInstanceExpr extends Expr, ConstructorCall, @classinstancexpr {
/** Gets the number of arguments provided to the constructor of the class instance creation expression. */

View File

@@ -272,6 +272,36 @@ private class PpCastExpr extends PpAst, CastExpr {
}
}
private class PpSafeCastExpr extends PpAst, SafeCastExpr {
override string getPart(int i) {
i = 1 and result = "as?"
}
override PpAst getChild(int i) {
i = 0 and result = this.getExpr()
or
i = 2 and result = this.getTypeExpr()
}
}
private class PpImplicitCastExpr extends PpAst, ImplicitCastExpr {
override PpAst getChild(int i) {
i = 0 and result = this.getExpr()
}
}
private class PpImplicitNotNullExpr extends PpAst, ImplicitNotNullExpr {
override PpAst getChild(int i) {
i = 0 and result = this.getExpr()
}
}
private class PpImplicitCoercionToUnitExpr extends PpAst, ImplicitCoercionToUnitExpr {
override PpAst getChild(int i) {
i = 0 and result = this.getExpr()
}
}
private class PpCall extends PpAst, Call {
override string getPart(int i) {
i = 1 and exists(this.getQualifier()) and result = "."

View File

@@ -12,7 +12,7 @@ private import IntegerGuards
/** Gets an expression that is always `null`. */
Expr alwaysNullExpr() {
result instanceof NullLiteral or
result.(CastExpr).getExpr() = alwaysNullExpr()
result.(CastingExpr).getExpr() = alwaysNullExpr()
}
/** Gets an equality test between an expression `e` and an enum constant `c`. */
@@ -62,6 +62,12 @@ Expr clearlyNotNullExpr(Expr reason) {
or
result.(CastExpr).getExpr() = clearlyNotNullExpr(reason)
or
result.(ImplicitCastExpr).getExpr() = clearlyNotNullExpr(reason)
or
result instanceof ImplicitNotNullExpr
or
result instanceof ImplicitCoercionToUnitExpr
or
result.(AssignExpr).getSource() = clearlyNotNullExpr(reason)
or
exists(ConditionalExpr c, Expr r1, Expr r2 |

View File

@@ -47,7 +47,9 @@ Expr nullExpr() {
result instanceof NullLiteral or
result.(ChooseExpr).getAResultExpr() = nullExpr() or
result.(AssignExpr).getSource() = nullExpr() or
result.(CastExpr).getExpr() = nullExpr()
result.(CastExpr).getExpr() = nullExpr() or
result.(ImplicitCastExpr).getExpr() = nullExpr() or
result instanceof SafeCastExpr
}
/** An expression of a boxed type that is implicitly unboxed. */
@@ -112,7 +114,8 @@ predicate dereference(Expr e) {
or
exists(ArrayAccess aa | aa.getArray() = e)
or
exists(CastExpr cast |
exists(CastingExpr cast |
(cast instanceof CastExpr or cast instanceof ImplicitCastExpr) and
cast.getExpr() = e and
e.getType() instanceof BoxedType and
cast.getType() instanceof PrimitiveType

View File

@@ -360,8 +360,13 @@ private predicate safeCast(Type fromtyp, Type totyp) {
/**
* A cast that can be ignored for the purpose of range analysis.
*/
private class RangeAnalysisSafeCastExpr extends CastExpr {
RangeAnalysisSafeCastExpr() { safeCast(getExpr().getType(), getType()) }
private class RangeAnalysisSafeCastingExpr extends CastingExpr {
RangeAnalysisSafeCastingExpr() {
safeCast(getExpr().getType(), getType()) or
this instanceof ImplicitCastExpr or
this instanceof ImplicitNotNullExpr or
this instanceof ImplicitCoercionToUnitExpr
}
}
/**
@@ -380,9 +385,9 @@ private predicate typeBound(Type typ, int lowerbound, int upperbound) {
/**
* A cast to a small integral type that may overflow or underflow.
*/
private class NarrowingCastExpr extends CastExpr {
NarrowingCastExpr() {
not this instanceof RangeAnalysisSafeCastExpr and
private class NarrowingCastingExpr extends CastingExpr {
NarrowingCastingExpr() {
not this instanceof RangeAnalysisSafeCastingExpr and
typeBound(getType(), _, _)
}
@@ -412,7 +417,7 @@ private predicate boundFlowStep(Expr e2, Expr e1, int delta, boolean upper) {
valueFlowStep(e2, e1, delta) and
(upper = true or upper = false)
or
e2.(RangeAnalysisSafeCastExpr).getExpr() = e1 and
e2.(RangeAnalysisSafeCastingExpr).getExpr() = e1 and
delta = 0 and
(upper = true or upper = false)
or
@@ -796,7 +801,7 @@ private predicate baseBound(Expr e, int b, boolean upper) {
* For `upper = true` this means that the cast will not overflow and for
* `upper = false` this means that the cast will not underflow.
*/
private predicate safeNarrowingCast(NarrowingCastExpr cast, boolean upper) {
private predicate safeNarrowingCast(NarrowingCastingExpr cast, boolean upper) {
exists(int bound | bounded(cast.getExpr(), any(ZeroBound zb), bound, upper, _, _, _) |
upper = true and bound <= cast.getUpperBound()
or
@@ -806,7 +811,7 @@ private predicate safeNarrowingCast(NarrowingCastExpr cast, boolean upper) {
pragma[noinline]
private predicate boundedCastExpr(
NarrowingCastExpr cast, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
NarrowingCastingExpr cast, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
Reason reason
) {
bounded(cast.getExpr(), b, delta, upper, fromBackEdge, origdelta, reason)
@@ -870,7 +875,7 @@ private predicate bounded(
delta = d / factor
)
or
exists(NarrowingCastExpr cast |
exists(NarrowingCastingExpr cast |
cast = e and
safeNarrowingCast(cast, upper.booleanNot()) and
boundedCastExpr(cast, b, delta, upper, fromBackEdge, origdelta, reason)

View File

@@ -1114,8 +1114,11 @@ class SsaPhiNode extends SsaVariable, TSsaPhiNode {
}
}
private class RefTypeCastExpr extends CastExpr {
RefTypeCastExpr() { this.getType() instanceof RefType }
private class RefTypeCastingExpr extends CastingExpr {
RefTypeCastingExpr() {
this.getType() instanceof RefType and
not this instanceof SafeCastExpr
}
}
/**
@@ -1130,5 +1133,5 @@ Expr sameValue(SsaVariable v, VarAccess va) {
or
result.(AssignExpr).getSource() = sameValue(v, va)
or
result.(RefTypeCastExpr).getExpr() = sameValue(v, va)
result.(RefTypeCastingExpr).getExpr() = sameValue(v, va)
}

View File

@@ -107,7 +107,7 @@ private predicate step(TypeFlowNode n1, TypeFlowNode n2) {
or
n2.asExpr() = n1.asSsa().getAUse()
or
n2.asExpr().(CastExpr).getExpr() = n1.asExpr() and
n2.asExpr().(CastingExpr).getExpr() = n1.asExpr() and
not n2.asExpr().getType() instanceof PrimitiveType
or
n2.asExpr().(AssignExpr).getSource() = n1.asExpr() and
@@ -214,7 +214,7 @@ private predicate upcastCand(TypeFlowNode n, RefType t, RefType t1, RefType t2)
(
exists(Variable v | v.getAnAssignedValue() = n.asExpr() and t2 = v.getType().getErasure())
or
exists(CastExpr c | c.getExpr() = n.asExpr() and t2 = c.getType().getErasure())
exists(CastingExpr c | c.getExpr() = n.asExpr() and t2 = c.getType().getErasure())
or
exists(ReturnStmt ret |
ret.getResult() = n.asExpr() and t2 = ret.getEnclosingCallable().getReturnType().getErasure()
@@ -272,7 +272,7 @@ private predicate upcastEnhancedForStmt(BaseSsaUpdate v, RefType t) {
}
private predicate downcastSuccessorAux(
CastExpr cast, BaseSsaVariable v, RefType t, RefType t1, RefType t2
CastingExpr cast, BaseSsaVariable v, RefType t, RefType t1, RefType t2
) {
cast.getExpr() = v.getAUse() and
t = cast.getType() and
@@ -284,7 +284,7 @@ private predicate downcastSuccessorAux(
* Holds if `va` is an access to a value that has previously been downcast to `t`.
*/
private predicate downcastSuccessor(VarAccess va, RefType t) {
exists(CastExpr cast, BaseSsaVariable v, RefType t1, RefType t2 |
exists(CastingExpr cast, BaseSsaVariable v, RefType t1, RefType t2 |
downcastSuccessorAux(pragma[only_bind_into](cast), v, t, t1, t2) and
t1.getASourceSupertype+() = t2 and
va = v.getAUse() and

View File

@@ -224,7 +224,7 @@ predicate compatibleTypes(Type t1, Type t2) {
/** A node that performs a type cast. */
class CastNode extends ExprNode {
CastNode() { this.getExpr() instanceof CastExpr }
CastNode() { this.getExpr() instanceof CastingExpr }
}
private newtype TDataFlowCallable =

View File

@@ -144,7 +144,7 @@ predicate simpleLocalFlowStep(Node node1, Node node2) {
or
ThisFlow::adjacentThisRefs(node1.(PostUpdateNode).getPreUpdateNode(), node2)
or
node2.asExpr().(CastExpr).getExpr() = node1.asExpr()
node2.asExpr().(CastingExpr).getExpr() = node1.asExpr()
or
node2.asExpr().(ChooseExpr).getAResultExpr() = node1.asExpr()
or

View File

@@ -49,7 +49,7 @@ private predicate unknownSign(Expr e) {
or
exists(LongLiteral lit | lit = e and not exists(lit.getValue().toFloat()))
or
exists(CastExpr cast, Type fromtyp |
exists(CastingExpr cast, Type fromtyp |
cast = e and
fromtyp = cast.getSourceType() and
not fromtyp instanceof NumericOrCharType

View File

@@ -27,7 +27,7 @@ module Private {
class LongLiteral = J::LongLiteral;
class CastExpr extends J::CastExpr {
class CastingExpr extends J::CastingExpr {
/** Gets the source type of this cast. */
J::Type getSourceType() { result = this.getExpr().getType() }
}
@@ -307,7 +307,7 @@ private module Impl {
result = e.(PostIncExpr).getExpr() or
result = e.(PostDecExpr).getExpr() or
result = e.(ChooseExpr).getAResultExpr() or
result = e.(CastExpr).getExpr()
result = e.(CastingExpr).getExpr()
}
Expr getARead(SsaVariable v) { result = v.getAUse() }

View File

@@ -139,7 +139,7 @@ class JAXAnnotationReflectivelyConstructedClass extends ReflectivelyConstructedC
class DeserializedClass extends ReflectivelyConstructedClass {
DeserializedClass() {
exists(CastExpr cast, ReadObjectMethod readObject |
exists(CastingExpr cast, ReadObjectMethod readObject |
cast.getExpr().(MethodAccess).getMethod() = readObject
|
hasDescendant(cast.getType(), this)

View File

@@ -200,7 +200,7 @@ private predicate flowStep(RelevantNode n1, RelevantNode n2) {
n2.asExpr().(MethodAccess).getMethod() = getValue
)
or
n2.asExpr().(CastExpr).getExpr() = n1.asExpr()
n2.asExpr().(CastingExpr).getExpr() = n1.asExpr()
or
n2.asExpr().(ChooseExpr).getAResultExpr() = n1.asExpr()
or

View File

@@ -98,7 +98,7 @@ private predicate step(Node n1, Node n2) {
or
n2.asExpr().(FieldRead).getField() = n1.(FieldValueNode).getField()
or
n2.asExpr().(CastExpr).getExpr() = n1.asExpr()
n2.asExpr().(CastingExpr).getExpr() = n1.asExpr()
or
n2.asExpr().(ChooseExpr).getAResultExpr() = n1.asExpr()
or

View File

@@ -37,7 +37,7 @@ private Expr getRunnerArgument(MethodAccess ma, Method runmethod) {
result = ma.getArgument(param)
)
or
getRunnerArgument(ma, runmethod).(CastExpr).getExpr() = result
getRunnerArgument(ma, runmethod).(CastingExpr).getExpr() = result
or
pragma[only_bind_out](getRunnerArgument(ma, runmethod))
.(VarAccess)

View File

@@ -50,7 +50,7 @@ private class MongoDbInjectionSink extends QueryInjectionSink {
this.asExpr() = call.getArgument(0)
)
or
exists(CastExpr c |
exists(CastingExpr c |
c.getExpr() = this.asExpr() and
c.getTypeExpr().getType().(RefType).hasQualifiedName("com.mongodb", "DBObject")
)

View File

@@ -12,7 +12,7 @@
import java
predicate usefulUpcast(CastExpr e) {
predicate usefulUpcast(CastingExpr e) {
// Upcasts that may be performed to affect resolution of methods or constructors.
exists(Call c, int i, Callable target |
c.getArgument(i) = e and
@@ -59,7 +59,8 @@ predicate usefulUpcast(CastExpr e) {
from Expr e, RefType src, RefType dest
where
exists(CastExpr cse | cse = e |
exists(CastingExpr cse | cse = e |
(cse instanceof CastExpr or cse instanceof SafeCastExpr) and
exists(cse.getLocation()) and
src = cse.getExpr().getType() and
dest = cse.getType()

View File

@@ -30,7 +30,7 @@ predicate isConstantExp(Expr e) {
)
or
// A cast expression is constant if its expression is.
exists(CastExpr c | c = e | isConstantExp(c.getExpr()))
exists(CastingExpr c | c = e | isConstantExp(c.getExpr()))
or
// Multiplication by 0 is constant.
exists(MulExpr m | m = e | eval(m.getAnOperand()) = 0)

View File

@@ -26,7 +26,7 @@ predicate delegatingSuperCall(Expr e, Method target) {
forall(Expr arg | arg = call.getAnArgument() | arg instanceof VarAccess)
)
or
delegatingSuperCall(e.(CastExpr).getExpr(), target)
delegatingSuperCall(e.(CastingExpr).getExpr(), target)
}
/**

View File

@@ -9,7 +9,7 @@ import semmle.code.java.frameworks.Mockito
private predicate flowsInto(Expr e, Variable v) {
e = v.getAnAssignedValue()
or
exists(CastExpr c | flowsInto(c, v) | e = c.getExpr())
exists(CastingExpr c | flowsInto(c, v) | e = c.getExpr())
or
exists(ConditionalExpr c | flowsInto(c, v) | e = c.getABranchExpr())
}

View File

@@ -37,7 +37,7 @@ predicate isKeyNext(Expr e, KeySetIterator it) {
ma.getQualifier().(VarAccess).getVariable() = it
)
or
isKeyNext(e.(CastExpr).getExpr(), it)
isKeyNext(e.(CastingExpr).getExpr(), it)
}
class Key extends LocalVariableDecl {

View File

@@ -14,7 +14,7 @@ private import semmle.code.java.controlflow.internal.GuardsLogic
predicate narrowerThanOrEqualTo(ArithExpr exp, NumType numType) {
exp.getType().(NumType).widerThan(numType)
implies
exists(CastExpr cast | cast.getAChildExpr() = exp | numType.widerThanOrEqualTo(cast.getType()))
exists(CastingExpr cast | cast.getAChildExpr() = exp | numType.widerThanOrEqualTo(cast.getType()))
}
private Guard sizeGuard(SsaVariable v, boolean branch, boolean upper) {
@@ -140,7 +140,7 @@ predicate upcastToWiderType(Expr e) {
|
exists(Variable v | v.getAnAssignedValue() = e and t2 = v.getType())
or
exists(CastExpr c | c.getExpr() = e and t2 = c.getType())
exists(CastingExpr c | c.getExpr() = e and t2 = c.getType())
or
exists(ReturnStmt ret | ret.getResult() = e and t2 = ret.getEnclosingCallable().getReturnType())
or
@@ -170,7 +170,7 @@ predicate overflowIrrelevant(Expr exp) {
*/
private predicate unlikelyNode(DataFlow::Node n) {
n.getTypeBound() instanceof TypeObject and
not exists(CastExpr cast |
not exists(CastingExpr cast |
DataFlow::localFlow(n, DataFlow::exprNode(cast.getExpr())) and
cast.getType() instanceof NumericOrCharType
)

View File

@@ -24,7 +24,7 @@ predicate flowStep(Expr decl, Expr init) {
init = f.getAnAssignedValue()
)
or
decl.(CastExpr).getExpr() = init
decl.(CastingExpr).getExpr() = init
}
predicate excludedInit(Type t, Expr decl) {

View File

@@ -33,7 +33,7 @@ predicate guardedByInstanceOf(VarAccess e, RefType t) {
)
}
from CastExpr e, CollectionType c, CollectionType coll, string abstractName, string concreteName
from CastingExpr e, CollectionType c, CollectionType coll, string abstractName, string concreteName
where
coll instanceof Interface and
c instanceof Class and

View File

@@ -85,7 +85,7 @@ private predicate confusinglyOverloaded(Method m, Method n) {
private predicate wrappedAccess(Expr e, MethodAccess ma) {
e = ma or
wrappedAccess(e.(CastExpr).getExpr(), ma)
wrappedAccess(e.(CastingExpr).getExpr(), ma)
}
private predicate delegate(Method caller, Method callee) {
@@ -99,7 +99,7 @@ private predicate delegate(Method caller, Method callee) {
arg = p.getAnAccess()
or
// The parameter is cast to a supertype.
arg.(CastExpr).getExpr() = p.getAnAccess() and
arg.(CastingExpr).getExpr() = p.getAnAccess() and
arg.getType().(RefType).getASubtype() = p.getType()
)
)

View File

@@ -11,8 +11,9 @@
import java
from CastExpr redundant, Type type
from CastingExpr redundant, Type type
where
(redundant instanceof CastExpr or redundant instanceof SafeCastExpr) and
redundant.getType() = type and
type = redundant.getExpr().getType()
select redundant, "This cast is redundant - the expression is already of type '" + type + "'."