Kotlin: Add support for more type operators

This commit is contained in:
Ian Lynagh
2021-10-28 13:49:42 +01:00
parent d247e4fcff
commit ba7a7535e9
7 changed files with 102 additions and 9 deletions

View File

@@ -1475,8 +1475,28 @@ class X {
} }
} }
fun extractTypeAccess(t: IrType, parent: Label<out DbExprparent>, idx: Int, elementForLocation: IrElement) {
// TODO: elementForLocation allows us to give some sort of
// location, but a proper location for the type access will
// require upstream changes
val type = useType(t)
val id = tw.getFreshIdLabel<DbUnannotatedtypeaccess>()
tw.writeExprs_unannotatedtypeaccess(id, type.javaResult.id, type.kotlinResult.id, parent, idx)
val locId = tw.getLocation(elementForLocation)
tw.writeHasLocation(id, locId)
}
fun extractTypeOperatorCall(e: IrTypeOperatorCall, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int) { fun extractTypeOperatorCall(e: IrTypeOperatorCall, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int) {
when(e.operator) { when(e.operator) {
IrTypeOperator.CAST -> {
val id = tw.getFreshIdLabel<DbCastexpr>()
val locId = tw.getLocation(e)
val type = useType(e.type)
tw.writeExprs_castexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx)
tw.writeHasLocation(id, locId)
extractTypeAccess(e.typeOperand, id, 0, e)
extractExpressionExpr(e.argument, callable, id, 1)
}
IrTypeOperator.INSTANCEOF -> { IrTypeOperator.INSTANCEOF -> {
val id = tw.getFreshIdLabel<DbInstanceofexpr>() val id = tw.getFreshIdLabel<DbInstanceofexpr>()
val locId = tw.getLocation(e) val locId = tw.getLocation(e)
@@ -1484,10 +1504,16 @@ class X {
tw.writeExprs_instanceofexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx) tw.writeExprs_instanceofexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx)
tw.writeHasLocation(id, locId) tw.writeHasLocation(id, locId)
extractExpressionExpr(e.argument, callable, id, 0) extractExpressionExpr(e.argument, callable, id, 0)
val typeArg = useType(e.typeOperand) extractTypeAccess(e.typeOperand, id, 1, e)
val typeAccessId = tw.getFreshIdLabel<DbUnannotatedtypeaccess>() }
tw.writeExprs_unannotatedtypeaccess(typeAccessId, typeArg.javaResult.id, typeArg.kotlinResult.id, id, 1) IrTypeOperator.NOT_INSTANCEOF -> {
// TODO: Type access location val id = tw.getFreshIdLabel<DbNotinstanceofexpr>()
val locId = tw.getLocation(e)
val type = useType(e.type)
tw.writeExprs_notinstanceofexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx)
tw.writeHasLocation(id, locId)
extractExpressionExpr(e.argument, callable, id, 0)
extractTypeAccess(e.typeOperand, id, 1, e)
} }
else -> { else -> {
logger.warnElement(Severity.ErrorSevere, "Unrecognised IrTypeOperatorCall: " + e.render(), e) logger.warnElement(Severity.ErrorSevere, "Unrecognised IrTypeOperatorCall: " + e.render(), e)

View File

@@ -667,6 +667,8 @@ case @expr.kind of
| 74 = @errorexpr | 74 = @errorexpr
| 75 = @whenexpr | 75 = @whenexpr
| 76 = @getclassexpr | 76 = @getclassexpr
| 77 = @safecastexpr
| 78 = @notinstanceofexpr
; ;
/** Holds if this `when` expression was written as an `if` expression. */ /** Holds if this `when` expression was written as an `if` expression. */

View File

@@ -448,8 +448,12 @@ private module ControlFlowGraphImpl {
or or
this instanceof CastExpr this instanceof CastExpr
or or
this instanceof SafeCastExpr
or
this instanceof InstanceOfExpr and not this.(InstanceOfExpr).isPattern() this instanceof InstanceOfExpr and not this.(InstanceOfExpr).isPattern()
or or
this instanceof NotInstanceOfExpr
or
this instanceof LocalVariableDeclExpr and this instanceof LocalVariableDeclExpr and
not this = any(InstanceOfExpr ioe).getLocalVariableDeclExpr() not this = any(InstanceOfExpr ioe).getLocalVariableDeclExpr()
or or
@@ -526,8 +530,12 @@ private module ControlFlowGraphImpl {
or or
index = 0 and result = this.(CastExpr).getExpr() index = 0 and result = this.(CastExpr).getExpr()
or or
index = 0 and result = this.(SafeCastExpr).getExpr()
or
index = 0 and result = this.(InstanceOfExpr).getExpr() index = 0 and result = this.(InstanceOfExpr).getExpr()
or or
index = 0 and result = this.(NotInstanceOfExpr).getExpr()
or
index = 0 and result = this.(LocalVariableDeclExpr).getInit() index = 0 and result = this.(LocalVariableDeclExpr).getInit()
or or
index = 0 and result = this.(RValue).getQualifier() and not result instanceof TypeAccess index = 0 and result = this.(RValue).getQualifier() and not result instanceof TypeAccess
@@ -599,6 +607,8 @@ private module ControlFlowGraphImpl {
or or
result = first(n.(InstanceOfExpr).getExpr()) result = first(n.(InstanceOfExpr).getExpr())
or or
result = first(n.(NotInstanceOfExpr).getExpr())
or
result = first(n.(SynchronizedStmt).getExpr()) result = first(n.(SynchronizedStmt).getExpr())
or or
result = n and result = n and

View File

@@ -123,6 +123,18 @@ class CastConversionContext extends ConversionSite {
override string kind() { result = "cast context" } override string kind() { result = "cast context" }
} }
class SafeCastConversionContext extends ConversionSite {
SafeCastExpr c;
CastConversionContext() { this = c.getExpr() }
override Type getConversionTarget() { result = c.getType() }
override predicate isImplicit() { none() }
override string kind() { result = "safe cast context" }
}
/** /**
* A numeric conversion. For example, `a * b` converts `a` and * A numeric conversion. For example, `a * b` converts `a` and
* `b` to have an appropriate numeric type. * `b` to have an appropriate numeric type.

View File

@@ -1152,6 +1152,21 @@ class CastExpr extends Expr, @castexpr {
override string getAPrimaryQlClass() { result = "CastExpr" } 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) }
/** Gets a printable representation of this expression. */
override string toString() { result = "... as? ..." }
override string getAPrimaryQlClass() { result = "SafeCastExpr" }
}
/** A class instance creation expression. */ /** A class instance creation expression. */
class ClassInstanceExpr extends Expr, ConstructorCall, @classinstancexpr { class ClassInstanceExpr extends Expr, ConstructorCall, @classinstancexpr {
/** Gets the number of arguments provided to the constructor of the class instance creation expression. */ /** Gets the number of arguments provided to the constructor of the class instance creation expression. */
@@ -1442,6 +1457,28 @@ class InstanceOfExpr extends Expr, @instanceofexpr {
override string getAPrimaryQlClass() { result = "InstanceOfExpr" } override string getAPrimaryQlClass() { result = "InstanceOfExpr" }
} }
// TODO: Should this be desugared into instanceof.not()?
// Note expressions/IrTypeOperatorCall.kt says:
// NOT_INSTANCEOF, // TODO drop and replace with `INSTANCEOF<T>(x).not()`?
/** An `instanceof` expression. */
class NotInstanceOfExpr extends Expr, @notinstanceofexpr {
/** Gets the expression on the left-hand side of the `!is` operator. */
Expr getExpr() {
result.isNthChildOf(this, 0)
}
/** Gets the access to the type on the right-hand side of the `!is` operator. */
Expr getTypeName() { result.isNthChildOf(this, 1) }
/** Gets the type this `!is` expression checks for. */
RefType getCheckedType() { result = getTypeName().getType() }
/** Gets a printable representation of this expression. */
override string toString() { result = "... !is ..." }
override string getAPrimaryQlClass() { result = "NotInstanceOfExpr" }
}
/** /**
* A local variable declaration expression. * A local variable declaration expression.
* *

View File

@@ -360,8 +360,8 @@ private predicate safeCast(Type fromtyp, Type totyp) {
/** /**
* A cast that can be ignored for the purpose of range analysis. * A cast that can be ignored for the purpose of range analysis.
*/ */
private class SafeCastExpr extends CastExpr { private class RangeAnalysisSafeCastExpr extends CastExpr {
SafeCastExpr() { safeCast(getExpr().getType(), getType()) } RangeAnalysisSafeCastExpr() { safeCast(getExpr().getType(), getType()) }
} }
/** /**
@@ -382,7 +382,7 @@ private predicate typeBound(Type typ, int lowerbound, int upperbound) {
*/ */
private class NarrowingCastExpr extends CastExpr { private class NarrowingCastExpr extends CastExpr {
NarrowingCastExpr() { NarrowingCastExpr() {
not this instanceof SafeCastExpr and not this instanceof RangeAnalysisSafeCastExpr and
typeBound(getType(), _, _) typeBound(getType(), _, _)
} }
@@ -412,7 +412,7 @@ private predicate boundFlowStep(Expr e2, Expr e1, int delta, boolean upper) {
valueFlowStep(e2, e1, delta) and valueFlowStep(e2, e1, delta) and
(upper = true or upper = false) (upper = true or upper = false)
or or
e2.(SafeCastExpr).getExpr() = e1 and e2.(RangeAnalysisSafeCastExpr).getExpr() = e1 and
delta = 0 and delta = 0 and
(upper = true or upper = false) (upper = true or upper = false)
or or

View File

@@ -54,8 +54,15 @@
| exprs.kt:40:5:40:22 | b6 | LocalVariableDeclExpr | | exprs.kt:40:5:40:22 | b6 | LocalVariableDeclExpr |
| exprs.kt:40:14:40:15 | i1 | VarAccess | | exprs.kt:40:14:40:15 | i1 | VarAccess |
| exprs.kt:40:14:40:22 | ...instanceof... | InstanceOfExpr | | exprs.kt:40:14:40:22 | ...instanceof... | InstanceOfExpr |
| exprs.kt:40:14:40:22 | int | TypeAccess |
| exprs.kt:41:5:41:23 | b7 | LocalVariableDeclExpr | | exprs.kt:41:5:41:23 | b7 | LocalVariableDeclExpr |
| exprs.kt:41:14:41:15 | i1 | VarAccess |
| exprs.kt:41:14:41:23 | ... !is ... | NotInstanceOfExpr |
| exprs.kt:41:14:41:23 | int | TypeAccess |
| exprs.kt:42:5:42:26 | b8 | LocalVariableDeclExpr | | exprs.kt:42:5:42:26 | b8 | LocalVariableDeclExpr |
| exprs.kt:42:14:42:15 | b7 | VarAccess |
| exprs.kt:42:14:42:26 | (...)... | CastExpr |
| exprs.kt:42:14:42:26 | boolean | TypeAccess |
| exprs.kt:43:5:43:35 | str1 | LocalVariableDeclExpr | | exprs.kt:43:5:43:35 | str1 | LocalVariableDeclExpr |
| exprs.kt:43:25:43:34 | string lit | StringLiteral | | exprs.kt:43:25:43:34 | string lit | StringLiteral |
| exprs.kt:44:5:44:36 | str2 | LocalVariableDeclExpr | | exprs.kt:44:5:44:36 | str2 | LocalVariableDeclExpr |
@@ -74,4 +81,3 @@
| exprs.kt:53:9:53:18 | n | VarAccess | | exprs.kt:53:9:53:18 | n | VarAccess |
| exprs.kt:54:27:54:31 | new C(...) | ClassInstanceExpr | | exprs.kt:54:27:54:31 | new C(...) | ClassInstanceExpr |
| exprs.kt:54:29:54:30 | 42 | IntegerLiteral | | exprs.kt:54:29:54:30 | 42 | IntegerLiteral |
| file://:0:0:0:0 | int | TypeAccess |