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

Skip to content

Commit f95effc

Browse files
smowtonigfoo
authored andcommitted
Always extract ValueEQ/NEExpr for Kotlin ==/!=
I introduce AnyEqualsExpr for either reference or value equality and AnyEqualityTest for the same concept including not-equals operators, and use them wherever the written QL clearly doesn't care about the difference between reference and value comparison, typically because it is concerned with testing against null or against a primitive constant.
1 parent a120fab commit f95effc

27 files changed

Lines changed: 118 additions & 80 deletions

java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,15 +1278,9 @@ open class KotlinFileExtractor(
12781278
}
12791279
// != gets desugared into not and ==. Here we resugar it.
12801280
c.origin == IrStatementOrigin.EXCLEQ && isFunction("kotlin", "Boolean", "not") && c.valueArgumentsCount == 0 && dr != null && dr is IrCall && isBuiltinCallInternal(dr, "EQEQ") -> {
1281-
var id: Label<out DbExpr>
1281+
var id = tw.getFreshIdLabel<DbValueneexpr>()
12821282
val type = useType(c.type)
1283-
if (isShallowEqualityTest(dr)) {
1284-
id = tw.getFreshIdLabel<DbNeexpr>()
1285-
tw.writeExprs_neexpr(id, type.javaResult.id, parent, idx)
1286-
} else {
1287-
id = tw.getFreshIdLabel<DbValueneexpr>()
1288-
tw.writeExprs_valueneexpr(id, type.javaResult.id, parent, idx)
1289-
}
1283+
tw.writeExprs_valueneexpr(id, type.javaResult.id, parent, idx)
12901284
tw.writeExprsKotlinType(id, type.kotlinResult.id)
12911285
binOp(id, dr, callable, enclosingStmt)
12921286
}
@@ -1351,15 +1345,9 @@ open class KotlinFileExtractor(
13511345
if(c.origin != IrStatementOrigin.EQEQ) {
13521346
logger.errorElement("Unexpected origin for EQEQ: ${c.origin}", c)
13531347
}
1354-
var id: Label<out DbExpr>
1348+
var id = tw.getFreshIdLabel<DbValueeqexpr>()
13551349
val type = useType(c.type)
1356-
if (isShallowEqualityTest(c)) {
1357-
id = tw.getFreshIdLabel<DbEqexpr>()
1358-
tw.writeExprs_eqexpr(id, type.javaResult.id, parent, idx)
1359-
} else {
1360-
id = tw.getFreshIdLabel<DbValueeqexpr>()
1361-
tw.writeExprs_valueeqexpr(id, type.javaResult.id, parent, idx)
1362-
}
1350+
tw.writeExprs_valueeqexpr(id, type.javaResult.id, parent, idx)
13631351
tw.writeExprsKotlinType(id, type.kotlinResult.id)
13641352
binOp(id, c, callable, enclosingStmt)
13651353
}

java/ql/examples/snippets/eq_true.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99

1010
import java
1111

12-
from EqualityTest eq
12+
from AnyEqualityTest eq
1313
where eq.getAnOperand() instanceof BooleanLiteral
1414
select eq

java/ql/lib/semmle/code/java/Expr.qll

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -225,12 +225,12 @@ class CompileTimeConstantExpr extends Expr {
225225
)
226226
or
227227
(
228-
b instanceof EQExpr and
228+
b instanceof AnyEqualsExpr and
229229
if left = right then result = true else result = false
230230
)
231231
or
232232
(
233-
b instanceof NEExpr and
233+
b instanceof AnyNotEqualsExpr and
234234
if left != right then result = true else result = false
235235
)
236236
)
@@ -242,12 +242,12 @@ class CompileTimeConstantExpr extends Expr {
242242
right = b.getRightOperand().(CompileTimeConstantExpr).getBooleanValue()
243243
|
244244
(
245-
b instanceof EQExpr and
245+
b instanceof AnyEqualsExpr and
246246
if left = right then result = true else result = false
247247
)
248248
or
249249
(
250-
b instanceof NEExpr and
250+
b instanceof AnyNotEqualsExpr and
251251
if left != right then result = true else result = false
252252
)
253253
or
@@ -269,15 +269,18 @@ class CompileTimeConstantExpr extends Expr {
269269
/*
270270
* JLS 15.28 specifies that compile-time `String` constants are interned. Therefore `==`
271271
* equality can be interpreted as equality over the constant values, not the references.
272+
*
273+
* Kotlin's `==` and `===` operators will return the same result for `String`s, so they
274+
* can be handled alike:
272275
*/
273276

274277
(
275-
b instanceof EQExpr and
278+
b instanceof AnyEqualsExpr and
276279
if left = right then result = true else result = false
277280
)
278281
or
279282
(
280-
b instanceof NEExpr and
283+
b instanceof AnyNotEqualsExpr and
281284
if left != right then result = true else result = false
282285
)
283286
)
@@ -359,7 +362,7 @@ class CompileTimeConstantExpr extends Expr {
359362
or
360363
b instanceof XorBitwiseExpr and result = v1.bitXor(v2)
361364
// No `int` value for `AndLogicalExpr` or `OrLogicalExpr`.
362-
// No `int` value for `LTExpr`, `GTExpr`, `LEExpr`, `GEExpr`, `EQExpr` or `NEExpr`.
365+
// No `int` value for `LTExpr`, `GTExpr`, `LEExpr`, `GEExpr`, `AnyEqualsExpr` or `AnyNotEqualsExpr`.
363366
)
364367
or
365368
// Ternary conditional, with compile-time constant condition.
@@ -952,7 +955,7 @@ class GEExpr extends BinaryExpr, @geexpr {
952955
override string getAPrimaryQlClass() { result = "GEExpr" }
953956
}
954957

955-
/** A binary expression using the `==` operator. */
958+
/** A binary expression using Java's `==` or Kotlin's `===` operator. */
956959
class EQExpr extends BinaryExpr, @eqexpr {
957960
override string getOp() { result = " == " }
958961

@@ -961,12 +964,12 @@ class EQExpr extends BinaryExpr, @eqexpr {
961964

962965
/** A binary expression using the Kotlin `==` operator, semantically equivalent to `Objects.equals`. */
963966
class ValueEQExpr extends BinaryExpr, @valueeqexpr {
964-
override string getOp() { result = " == " }
967+
override string getOp() { result = " (value equals) " }
965968

966969
override string getAPrimaryQlClass() { result = "ValueEQExpr" }
967970
}
968971

969-
/** A binary expression using the `!=` operator. */
972+
/** A binary expression using Java's `!=` or Kotlin's `!==` operator. */
970973
class NEExpr extends BinaryExpr, @neexpr {
971974
override string getOp() { result = " != " }
972975

@@ -975,11 +978,35 @@ class NEExpr extends BinaryExpr, @neexpr {
975978

976979
/** A binary expression using the Kotlin `!=` operator, semantically equivalent to `Objects.equals`. */
977980
class ValueNEExpr extends BinaryExpr, @valueneexpr {
978-
override string getOp() { result = " != " }
981+
override string getOp() { result = " (value not-equals) " }
979982

980983
override string getAPrimaryQlClass() { result = "ValueNEExpr" }
981984
}
982985

986+
/**
987+
* A binary expression using either Java or Kotlin's `==` operator.
988+
*
989+
* This might test for reference equality or might function like `Objects.equals`. If you
990+
* need to distinguish them, use `EQExpr` or `ValueEQExpr` instead.
991+
*/
992+
class AnyEqualsExpr extends BinaryExpr {
993+
AnyEqualsExpr() {
994+
this instanceof EQExpr or this instanceof ValueEQExpr
995+
}
996+
}
997+
998+
/**
999+
* A binary expression using either Java or Kotlin's `!=` operator.
1000+
*
1001+
* This might test for reference equality or might function like `Objects.equals`. If you
1002+
* need to distinguish them, use `EQExpr` or `ValueEQExpr` instead.
1003+
*/
1004+
class AnyNotEqualsExpr extends BinaryExpr {
1005+
AnyNotEqualsExpr() {
1006+
this instanceof NEExpr or this instanceof ValueNEExpr
1007+
}
1008+
}
1009+
9831010
/**
9841011
* A bitwise expression.
9851012
*
@@ -1068,19 +1095,40 @@ class GreaterThanComparison extends ComparisonExpr {
10681095

10691096
/**
10701097
* An equality test is a binary expression using
1071-
* the `==` or `!=` operator.
1098+
* Java's `==` or `!=` operators, or Kotlin's `==`, `!=`, `===` or `!==` operators.
1099+
*
1100+
* This could be a reference- or a value-in/equality test.
10721101
*/
1073-
class EqualityTest extends BinaryExpr {
1074-
EqualityTest() {
1102+
class AnyEqualityTest extends BinaryExpr {
1103+
AnyEqualityTest() {
10751104
this instanceof EQExpr or
1076-
this instanceof NEExpr
1105+
this instanceof NEExpr or
1106+
this instanceof ValueEQExpr or
1107+
this instanceof ValueNEExpr
10771108
}
10781109

10791110
/** Gets a boolean indicating whether this is `==` (true) or `!=` (false). */
10801111
boolean polarity() {
10811112
result = true and this instanceof EQExpr
10821113
or
10831114
result = false and this instanceof NEExpr
1115+
or
1116+
result = true and this instanceof ValueEQExpr
1117+
or
1118+
result = false and this instanceof ValueNEExpr
1119+
}
1120+
}
1121+
1122+
/**
1123+
* An equality test is a binary expression using
1124+
* Java's `==` or `!=` operator.
1125+
*
1126+
* If either operand is a reference type, this is a reference-in/equality test.
1127+
*/
1128+
class EqualityTest extends AnyEqualityTest {
1129+
EqualityTest() {
1130+
this instanceof EQExpr or
1131+
this instanceof NEExpr
10841132
}
10851133
}
10861134

java/ql/lib/semmle/code/java/controlflow/Guards.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ private predicate guardControls_v3(Guard guard, BasicBlock controlled, boolean b
242242
}
243243

244244
private predicate equalityGuard(Guard g, Expr e1, Expr e2, boolean polarity) {
245-
exists(EqualityTest eqtest |
245+
exists(AnyEqualityTest eqtest |
246246
eqtest = g and
247247
polarity = eqtest.polarity() and
248248
eqtest.hasOperands(e1, e2)

java/ql/lib/semmle/code/java/controlflow/UnreachableBlocks.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,12 @@ class ConstantExpr extends Expr {
133133
)
134134
or
135135
(
136-
b instanceof EQExpr and
136+
b instanceof AnyEqualsExpr and
137137
if left = right then result = true else result = false
138138
)
139139
or
140140
(
141-
b instanceof NEExpr and
141+
b instanceof AnyNotEqualsExpr and
142142
if left != right then result = true else result = false
143143
)
144144
)

java/ql/lib/semmle/code/java/controlflow/internal/GuardsLogic.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ predicate implies_v1(Guard g1, boolean b1, Guard g2, boolean b2) {
3535
b1 = b2.booleanNot() and
3636
b1 = [true, false]
3737
or
38-
exists(EqualityTest eqtest, boolean polarity, BooleanLiteral boollit |
38+
exists(AnyEqualityTest eqtest, boolean polarity, BooleanLiteral boollit |
3939
eqtest = g1 and
4040
eqtest.hasOperands(g2, boollit) and
4141
eqtest.polarity() = polarity and

java/ql/lib/semmle/code/java/dataflow/IntegerGuards.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class IntComparableExpr extends Expr {
4242
*/
4343
pragma[nomagic]
4444
Expr integerGuard(IntComparableExpr e, boolean branch, int k, boolean is_k) {
45-
exists(EqualityTest eqtest, boolean polarity |
45+
exists(AnyEqualityTest eqtest, boolean polarity |
4646
eqtest = result and
4747
eqtest.hasOperands(e, any(ConstantIntegerExpr c | c.getIntValue() = k)) and
4848
polarity = eqtest.polarity() and
@@ -53,7 +53,7 @@ Expr integerGuard(IntComparableExpr e, boolean branch, int k, boolean is_k) {
5353
)
5454
)
5555
or
56-
exists(EqualityTest eqtest, int val, Expr c, boolean upper |
56+
exists(AnyEqualityTest eqtest, int val, Expr c, boolean upper |
5757
k = e.relevantInt() and
5858
eqtest = result and
5959
eqtest.hasOperands(e, c) and

java/ql/lib/semmle/code/java/dataflow/NullGuards.qll

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Expr alwaysNullExpr() {
1717

1818
/** Gets an equality test between an expression `e` and an enum constant `c`. */
1919
Expr enumConstEquality(Expr e, boolean polarity, EnumConstant c) {
20-
exists(EqualityTest eqtest |
20+
exists(AnyEqualityTest eqtest |
2121
eqtest = result and
2222
eqtest.hasOperands(e, c.getAnAccess()) and
2323
polarity = eqtest.polarity()
@@ -33,8 +33,10 @@ InstanceOfExpr instanceofExpr(SsaVariable v, RefType type) {
3333
/**
3434
* Gets an expression of the form `v1 == v2` or `v1 != v2`.
3535
* The predicate is symmetric in `v1` and `v2`.
36+
*
37+
* Note this includes Kotlin's `==` and `!=` operators, which are value-equality tests.
3638
*/
37-
EqualityTest varEqualityTestExpr(SsaVariable v1, SsaVariable v2, boolean isEqualExpr) {
39+
AnyEqualityTest varEqualityTestExpr(SsaVariable v1, SsaVariable v2, boolean isEqualExpr) {
3840
result.hasOperands(v1.getAUse(), v2.getAUse()) and
3941
isEqualExpr = result.polarity()
4042
}
@@ -172,7 +174,7 @@ predicate nullCheckMethod(Method m, boolean branch, boolean isnull) {
172174
* is true, and non-null if `isnull` is false.
173175
*/
174176
Expr basicNullGuard(Expr e, boolean branch, boolean isnull) {
175-
exists(EqualityTest eqtest, boolean polarity |
177+
exists(AnyEqualityTest eqtest, boolean polarity |
176178
eqtest = result and
177179
eqtest.hasOperands(e, any(NullLiteral n)) and
178180
polarity = eqtest.polarity() and
@@ -191,7 +193,7 @@ Expr basicNullGuard(Expr e, boolean branch, boolean isnull) {
191193
nullCheckMethod(call.getMethod(), branch, isnull)
192194
)
193195
or
194-
exists(EqualityTest eqtest |
196+
exists(AnyEqualityTest eqtest |
195197
eqtest = result and
196198
eqtest.hasOperands(e, clearlyNotNullExpr()) and
197199
isnull = false and

java/ql/lib/semmle/code/java/dataflow/Nullness.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,13 @@ private predicate unboxed(Expr e) {
7070
or
7171
exists(AssignOp assign | assign.getSource() = e and assign.getType() instanceof PrimitiveType)
7272
or
73-
exists(EqualityTest eq |
73+
exists(AnyEqualityTest eq |
7474
eq.getAnOperand() = e and eq.getAnOperand().getType() instanceof PrimitiveType
7575
)
7676
or
7777
exists(BinaryExpr bin |
7878
bin.getAnOperand() = e and
79-
not bin instanceof EqualityTest and
79+
not bin instanceof AnyEqualityTest and
8080
bin.getType() instanceof PrimitiveType
8181
)
8282
or
@@ -653,7 +653,7 @@ private Expr trackingVarGuard(
653653
)
654654
)
655655
or
656-
exists(EqualityTest eqtest, boolean branch0, boolean polarity, BooleanLiteral boollit |
656+
exists(AnyEqualityTest eqtest, boolean branch0, boolean polarity, BooleanLiteral boollit |
657657
eqtest = result and
658658
eqtest.hasOperands(trackingVarGuard(trackssa, trackvar, kind, branch0, isA), boollit) and
659659
eqtest.polarity() = polarity and

java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ private predicate taintPreservingArgumentToQualifier(Method method, int arg) {
387387
/** A comparison or equality test with a constant. */
388388
private predicate comparisonStep(Expr tracked, Expr sink) {
389389
exists(Expr other |
390-
exists(BinaryExpr e | e instanceof ComparisonExpr or e instanceof EqualityTest |
390+
exists(BinaryExpr e | e instanceof ComparisonExpr or e instanceof AnyEqualityTest |
391391
e = sink and
392392
e.hasOperands(tracked, other)
393393
)

0 commit comments

Comments
 (0)