Skip to content

Commit a71de45

Browse files
authored
Merge pull request github#111 from github/igfoo/consistency
Kotlin: Binary operators and consistency queries
2 parents 67f632c + a258862 commit a71de45

File tree

11 files changed

+267
-88
lines changed

11 files changed

+267
-88
lines changed

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

Lines changed: 134 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ fun doFile(invocationTrapFile: String,
173173
}
174174

175175
fun <T> fakeLabel(): Label<T> {
176-
if (true) {
176+
if (false) {
177177
println("Fake label")
178178
} else {
179179
val sw = StringWriter()
@@ -1415,53 +1415,144 @@ open class KotlinFileExtractor(
14151415
}
14161416

14171417
fun extractCall(c: IrCall, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int) {
1418-
val exprId: Label<out DbExpr> = when (c.origin) {
1419-
PLUS -> {
1418+
fun isFunction(pkgName: String, className: String?, fName: String): Boolean {
1419+
val verbose = false
1420+
fun verboseln(s: String) { if(verbose) println(s) }
1421+
verboseln("Attempting match for $pkgName $className $fName")
1422+
val target = c.symbol.owner
1423+
if (target.name.asString() != fName) {
1424+
verboseln("No match as function name is ${target.name.asString()} not $fName")
1425+
return false
1426+
}
1427+
val extensionReceiverParameter = target.extensionReceiverParameter
1428+
val targetClass = if (extensionReceiverParameter == null) target.parent
1429+
else (extensionReceiverParameter.type as? IrSimpleType)?.classifier?.owner
1430+
val targetPkg =
1431+
if (className != null) {
1432+
if (targetClass !is IrClass) {
1433+
verboseln("No match as didn't find target class")
1434+
return false
1435+
}
1436+
if (targetClass.name.asString() != className) {
1437+
verboseln("No match as class name is ${targetClass.name.asString()} not $className")
1438+
return false
1439+
}
1440+
targetClass.parent
1441+
} else {
1442+
targetClass
1443+
}
1444+
if (targetPkg !is IrPackageFragment) {
1445+
verboseln("No match as didn't find target package")
1446+
return false
1447+
}
1448+
if (targetPkg.fqName.asString() != pkgName) {
1449+
verboseln("No match as class name is ${targetPkg.fqName.asString()} not $pkgName")
1450+
return false
1451+
}
1452+
verboseln("Match")
1453+
return true
1454+
}
1455+
1456+
fun binopDisp(id: Label<out DbExpr>) {
1457+
val locId = tw.getLocation(c)
1458+
tw.writeHasLocation(id, locId)
1459+
tw.writeCallableEnclosingExpr(id, callable)
1460+
1461+
val dr = c.dispatchReceiver
1462+
if(dr == null) {
1463+
logger.warnElement(Severity.ErrorSevere, "Dispatch receiver not found", c)
1464+
} else {
1465+
extractExpressionExpr(dr, callable, id, 0)
1466+
}
1467+
if(c.valueArgumentsCount < 1) {
1468+
logger.warnElement(Severity.ErrorSevere, "No RHS found", c)
1469+
} else {
1470+
if(c.valueArgumentsCount > 1) {
1471+
logger.warnElement(Severity.ErrorSevere, "Extra arguments found", c)
1472+
}
1473+
val arg = c.getValueArgument(0)
1474+
if(arg == null) {
1475+
logger.warnElement(Severity.ErrorSevere, "RHS null", c)
1476+
} else {
1477+
extractExpressionExpr(arg, callable, id, 1)
1478+
}
1479+
}
1480+
}
1481+
1482+
fun binop(id: Label<out DbExpr>) {
1483+
val locId = tw.getLocation(c)
1484+
tw.writeHasLocation(id, locId)
1485+
tw.writeCallableEnclosingExpr(id, callable)
1486+
1487+
val dr = c.dispatchReceiver
1488+
if(dr != null) {
1489+
logger.warnElement(Severity.ErrorSevere, "Unexpected dispatch receiver found", c)
1490+
}
1491+
if(c.valueArgumentsCount < 1) {
1492+
logger.warnElement(Severity.ErrorSevere, "No arguments found", c)
1493+
} else {
1494+
val lhs = c.getValueArgument(0)
1495+
if(lhs == null) {
1496+
logger.warnElement(Severity.ErrorSevere, "LHS null", c)
1497+
} else {
1498+
extractExpressionExpr(lhs, callable, id, 0)
1499+
}
1500+
if(c.valueArgumentsCount < 2) {
1501+
logger.warnElement(Severity.ErrorSevere, "No RHS found", c)
1502+
} else {
1503+
val rhs = c.getValueArgument(1)
1504+
if(rhs == null) {
1505+
logger.warnElement(Severity.ErrorSevere, "RHS null", c)
1506+
} else {
1507+
extractExpressionExpr(rhs, callable, id, 1)
1508+
}
1509+
}
1510+
if(c.valueArgumentsCount > 2) {
1511+
logger.warnElement(Severity.ErrorSevere, "Extra arguments found", c)
1512+
}
1513+
}
1514+
}
1515+
1516+
when {
1517+
c.origin == PLUS &&
1518+
(isFunction("kotlin", "Int", "plus") || isFunction("kotlin", "String", "plus")) -> {
14201519
val id = tw.getFreshIdLabel<DbAddexpr>()
14211520
val type = useType(c.type)
1422-
val locId = tw.getLocation(c)
14231521
tw.writeExprs_addexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx)
1424-
tw.writeHasLocation(id, locId)
1425-
tw.writeCallableEnclosingExpr(id, callable)
1522+
binopDisp(id)
14261523
id
14271524
}
1428-
MINUS -> {
1525+
c.origin == MINUS && isFunction("kotlin", "Int", "minus") -> {
14291526
val id = tw.getFreshIdLabel<DbSubexpr>()
14301527
val type = useType(c.type)
1431-
val locId = tw.getLocation(c)
14321528
tw.writeExprs_subexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx)
1433-
tw.writeHasLocation(id, locId)
1434-
tw.writeCallableEnclosingExpr(id, callable)
1529+
binopDisp(id)
14351530
id
14361531
}
1437-
DIV -> {
1532+
c.origin == DIV && isFunction("kotlin", "Int", "div") -> {
14381533
val id = tw.getFreshIdLabel<DbDivexpr>()
14391534
val type = useType(c.type)
1440-
val locId = tw.getLocation(c)
14411535
tw.writeExprs_divexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx)
1442-
tw.writeHasLocation(id, locId)
1443-
tw.writeCallableEnclosingExpr(id, callable)
1536+
binopDisp(id)
14441537
id
14451538
}
1446-
PERC -> {
1539+
c.origin == PERC && isFunction("kotlin", "Int", "rem") -> {
14471540
val id = tw.getFreshIdLabel<DbRemexpr>()
14481541
val type = useType(c.type)
1449-
val locId = tw.getLocation(c)
14501542
tw.writeExprs_remexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx)
1451-
tw.writeHasLocation(id, locId)
1452-
tw.writeCallableEnclosingExpr(id, callable)
1543+
binopDisp(id)
14531544
id
14541545
}
1455-
EQEQ -> {
1546+
c.origin == EQEQ && isFunction("kotlin.internal.ir", null, "EQEQ") -> {
14561547
val id = tw.getFreshIdLabel<DbEqexpr>()
14571548
val type = useType(c.type)
1458-
val locId = tw.getLocation(c)
14591549
tw.writeExprs_eqexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx)
1460-
tw.writeHasLocation(id, locId)
1461-
tw.writeCallableEnclosingExpr(id, callable)
1550+
binop(id)
14621551
id
14631552
}
1464-
EXCLEQ -> {
1553+
/*
1554+
TODO
1555+
c.origin == EXCLEQ -> {
14651556
val id = tw.getFreshIdLabel<DbNeexpr>()
14661557
val type = useType(c.type)
14671558
val locId = tw.getLocation(c)
@@ -1470,40 +1561,33 @@ open class KotlinFileExtractor(
14701561
tw.writeCallableEnclosingExpr(id, callable)
14711562
id
14721563
}
1473-
LT -> {
1564+
*/
1565+
c.origin == LT && isFunction("kotlin.internal.ir", null, "less") -> {
14741566
val id = tw.getFreshIdLabel<DbLtexpr>()
14751567
val type = useType(c.type)
1476-
val locId = tw.getLocation(c)
14771568
tw.writeExprs_ltexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx)
1478-
tw.writeHasLocation(id, locId)
1479-
tw.writeCallableEnclosingExpr(id, callable)
1569+
binop(id)
14801570
id
14811571
}
1482-
LTEQ -> {
1572+
c.origin == LTEQ && isFunction("kotlin.internal.ir", null, "lessOrEqual") -> {
14831573
val id = tw.getFreshIdLabel<DbLeexpr>()
14841574
val type = useType(c.type)
1485-
val locId = tw.getLocation(c)
14861575
tw.writeExprs_leexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx)
1487-
tw.writeHasLocation(id, locId)
1488-
tw.writeCallableEnclosingExpr(id, callable)
1576+
binop(id)
14891577
id
14901578
}
1491-
GT -> {
1579+
c.origin == GT && isFunction("kotlin.internal.ir", null, "greater") -> {
14921580
val id = tw.getFreshIdLabel<DbGtexpr>()
14931581
val type = useType(c.type)
1494-
val locId = tw.getLocation(c)
14951582
tw.writeExprs_gtexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx)
1496-
tw.writeHasLocation(id, locId)
1497-
tw.writeCallableEnclosingExpr(id, callable)
1583+
binop(id)
14981584
id
14991585
}
1500-
GTEQ -> {
1586+
c.origin == GTEQ && isFunction("kotlin.internal.ir", null, "greaterOrEqual") -> {
15011587
val id = tw.getFreshIdLabel<DbGeexpr>()
15021588
val type = useType(c.type)
1503-
val locId = tw.getLocation(c)
15041589
tw.writeExprs_geexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx)
1505-
tw.writeHasLocation(id, locId)
1506-
tw.writeCallableEnclosingExpr(id, callable)
1590+
binop(id)
15071591
id
15081592
}
15091593
else -> {
@@ -1519,16 +1603,17 @@ open class KotlinFileExtractor(
15191603
// type arguments at index -2, -3, ...
15201604
extractTypeArguments(c, id, callable, -2, true)
15211605
id
1522-
}
1523-
}
1524-
val dr = c.dispatchReceiver
1525-
if(dr != null) {
1526-
extractExpressionExpr(dr, callable, exprId, -1)
1527-
}
1528-
for(i in 0 until c.valueArgumentsCount) {
1529-
val arg = c.getValueArgument(i)
1530-
if(arg != null) {
1531-
extractExpressionExpr(arg, callable, exprId, i)
1606+
1607+
val dr = c.dispatchReceiver
1608+
if(dr != null) {
1609+
extractExpressionExpr(dr, callable, id, -1)
1610+
}
1611+
for(i in 0 until c.valueArgumentsCount) {
1612+
val arg = c.getValueArgument(i)
1613+
if(arg != null) {
1614+
extractExpressionExpr(arg, callable, id, i)
1615+
}
1616+
}
15321617
}
15331618
}
15341619
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import java
2+
3+
from BinaryExpr be, string reason
4+
where not exists(be.getLeftOperand()) and reason = "No left operand"
5+
or not exists(be.getRightOperand()) and reason = "No right operand"
6+
or exists(Expr e, int i | e.isNthChildOf(be, i) and i != 0 and i != 1 and reason = "Unexpected operand " + i.toString())
7+
or be.getOp() = " ?? " and reason = "No operator name"
8+
select be, reason
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import java
2+
3+
from UnaryExpr ue
4+
where not exists(ue.getExpr())
5+
or exists(Expr e, int i | e.isNthChildOf(ue, i) and i != 0)
6+
select ue

java/ql/consistency-queries/blocks.ql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import java
2+
3+
from BlockStmt b, Expr e
4+
where e.getParent() = b
5+
select b, e
6+
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import java
2+
3+
Element nthChildOf(Element e, int i) {
4+
result.(Expr).isNthChildOf(e, i) or
5+
result.(Stmt).isNthChildOf(e, i)
6+
}
7+
8+
predicate duplicateChildren(Element e, int i) {
9+
nthChildOf(e, i) != nthChildOf(e, i)
10+
// Bug?:
11+
and not e instanceof Method
12+
// Bug?:
13+
and not e instanceof Constructor
14+
}
15+
16+
predicate gapInChildren(Element e, int i) {
17+
exists(nthChildOf(e, i))
18+
and not exists(nthChildOf(e, i - 1))
19+
and exists(int j | j < i | exists(nthChildOf(e, j)))
20+
// TODO: Tighten this up:
21+
and not e instanceof Class
22+
// TODO: Tighten this up:
23+
and not e instanceof Interface
24+
// TODO: Tighten this up:
25+
and not e instanceof ClassInstanceExpr
26+
// TODO: Tighten this up:
27+
and not e instanceof TypeAccess
28+
// TODO: Tighten this up:
29+
and not e instanceof TryStmt
30+
// TODO: Tighten this up:
31+
and not e instanceof ForStmt
32+
// Kotlin bug?
33+
and not (e instanceof MethodAccess and e.getFile().getExtension() = "kt")
34+
}
35+
36+
predicate lateFirstChild(Element e, int i) {
37+
i > 0
38+
and exists(nthChildOf(e, i))
39+
and forex(int j | exists(nthChildOf(e, j)) | j >= i)
40+
// TODO: Tighten this up:
41+
and not e instanceof WildcardTypeAccess
42+
// TODO: Tighten this up:
43+
and not e instanceof LocalVariableDeclStmt
44+
// TODO: Tighten this up:
45+
and not e instanceof ForStmt
46+
}
47+
48+
from Element e, int i, string problem
49+
where problem = "duplicate" and duplicateChildren(e, i)
50+
or problem = "gap" and gapInChildren(e, i)
51+
or problem = "late" and lateFirstChild(e, i)
52+
select e, e.getPrimaryQlClasses(), i, problem, nthChildOf(e, i),
53+
concat(int j | exists(nthChildOf(e, j)) | j.toString(), ", ")

java/ql/consistency-queries/getAPrimaryQlClass.ql

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,5 @@ where t.getAPrimaryQlClass() = "???"
66
and not t instanceof TypeBound
77
// XMLLocatable doesn't extend Top (but probably should)
88
and not t instanceof XMLLocatable
9-
// Kotlin bug:
10-
and not t.(Type).toString() = "string"
119
select t,
1210
concat(t.getAPrimaryQlClass(), ",")

java/ql/test/kotlin/library-tests/controlflow/dominance/dominator.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
| Test.kt:13:4:13:4 | <Expr>; | Test.kt:13:8:13:9 | 10 |
1212
| Test.kt:14:10:16:3 | { ... } | Test.kt:15:4:15:4 | <Expr>; |
1313
| Test.kt:15:4:15:4 | <Expr>; | Test.kt:15:8:15:9 | 30 |
14-
| Test.kt:18:3:18:3 | <Expr>; | Test.kt:18:12:18:12 | y |
14+
| Test.kt:18:3:18:3 | <Expr>; | Test.kt:18:8:18:8 | x |
1515
| Test.kt:21:3:24:11 | <Expr>; | Test.kt:21:3:24:11 | when ... |
1616
| Test.kt:22:4:22:4 | <Expr>; | Test.kt:22:8:22:9 | 40 |
1717
| Test.kt:27:3:27:3 | <Expr>; | Test.kt:27:7:27:8 | 10 |
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
| exprs.kt:6:14:6:18 | ... + ... | exprs.kt:6:14:6:14 | x | exprs.kt:6:18:6:18 | y |
2+
| exprs.kt:7:14:7:18 | ... - ... | exprs.kt:7:14:7:14 | x | exprs.kt:7:18:7:18 | y |
3+
| exprs.kt:8:14:8:18 | ... / ... | exprs.kt:8:14:8:14 | x | exprs.kt:8:18:8:18 | y |
4+
| exprs.kt:9:14:9:18 | ... % ... | exprs.kt:9:14:9:14 | x | exprs.kt:9:18:9:18 | y |
5+
| exprs.kt:20:15:20:20 | ... == ... | exprs.kt:20:15:20:15 | x | exprs.kt:20:20:20:20 | y |
6+
| exprs.kt:22:15:22:19 | ... < ... | exprs.kt:22:15:22:15 | x | exprs.kt:22:19:22:19 | y |
7+
| exprs.kt:23:15:23:20 | ... <= ... | exprs.kt:23:15:23:15 | x | exprs.kt:23:20:23:20 | y |
8+
| exprs.kt:24:15:24:19 | ... > ... | exprs.kt:24:15:24:15 | x | exprs.kt:24:19:24:19 | y |
9+
| exprs.kt:25:15:25:20 | ... >= ... | exprs.kt:25:15:25:15 | x | exprs.kt:25:20:25:20 | y |
10+
| exprs.kt:50:16:50:26 | ... + ... | exprs.kt:50:16:50:19 | str1 | exprs.kt:50:23:50:26 | str2 |
11+
| exprs.kt:53:12:53:23 | ... > ... | exprs.kt:53:12:53:19 | variable | exprs.kt:53:23:53:23 | 0 |
12+
| exprs.kt:57:12:57:20 | ... + ... | exprs.kt:57:12:57:14 | 123 | exprs.kt:57:18:57:20 | 456 |

0 commit comments

Comments
 (0)