[Coverage] Support conditional expressions
This commit is contained in:
committed by
Sergey Bogolepov
parent
45a6c58d56
commit
70a7b61309
+41
-148
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.util.nameForIrSerialization
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
|
||||
/**
|
||||
* Collect all regions in the module.
|
||||
@@ -84,169 +83,63 @@ private class IrFunctionRegionsCollector(
|
||||
override fun visitFunction(declaration: IrFunction) {
|
||||
declaration.body?.let {
|
||||
recordRegion(it)
|
||||
visitBody(it)
|
||||
it.acceptChildrenVoid(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitExpression(expression: IrExpression) {
|
||||
collectRegions(expression)
|
||||
}
|
||||
override fun visitWhen(expression: IrWhen) {
|
||||
val branches = expression.branches
|
||||
branches.forEach {
|
||||
val condition = it.condition
|
||||
val result = it.result
|
||||
|
||||
override fun visitVariable(declaration: IrVariable) {
|
||||
recordRegion(declaration)
|
||||
declaration.initializer?.let { collectRegions(it) }
|
||||
}
|
||||
|
||||
override fun visitBody(body: IrBody) = when (body) {
|
||||
is IrExpressionBody -> body.acceptChildrenVoid(this)
|
||||
is IrBlockBody -> body.acceptChildrenVoid(this)
|
||||
else -> error("Unexpected function body type: $body")
|
||||
}
|
||||
|
||||
fun collectRegions(value: IrExpression): Unit = when (value) {
|
||||
is IrTypeOperatorCall -> collectTypeOperator(value)
|
||||
is IrCall -> collectCall(value)
|
||||
is IrConstructorCall -> collectCall(value)
|
||||
is IrDelegatingConstructorCall -> collectCall(value)
|
||||
is IrInstanceInitializerCall -> collectInstanceInitializerCall(value)
|
||||
is IrGetValue -> collectGetValue(value)
|
||||
is IrSetVariable -> collectSetVariable(value)
|
||||
is IrGetField -> collectGetField(value)
|
||||
is IrSetField -> collectSetField(value)
|
||||
is IrConst<*> -> collectConst(value)
|
||||
is IrReturn -> collectReturn(value)
|
||||
is IrWhen -> collectWhen(value)
|
||||
is IrThrow -> collectThrow(value)
|
||||
is IrTry -> collectTry(value)
|
||||
is IrReturnableBlock -> collectReturnableBlock(value)
|
||||
is IrContainerExpression -> collectContainerExpression(value)
|
||||
is IrWhileLoop -> collectWhileLoop(value)
|
||||
is IrDoWhileLoop -> collectDoWhileLoop(value)
|
||||
is IrVararg -> collectVararg(value)
|
||||
is IrBreak -> collectBreak(value)
|
||||
is IrContinue -> collectContinue(value)
|
||||
is IrGetObjectValue -> collectGetObjectValue(value)
|
||||
is IrFunctionReference -> collectFunctionReference(value)
|
||||
is IrSuspendableExpression -> collectSuspendableExpression(value)
|
||||
is IrSuspensionPoint -> collectSuspensionPoint(value)
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectInstanceInitializerCall(instanceInitializerCall: IrInstanceInitializerCall) {
|
||||
|
||||
}
|
||||
|
||||
private fun collectGetValue(getValue: IrGetValue) {
|
||||
recordRegion(getValue)
|
||||
}
|
||||
|
||||
private fun collectSetVariable(setVariable: IrSetVariable) {
|
||||
recordRegion(setVariable)
|
||||
setVariable.value.acceptVoid(this)
|
||||
}
|
||||
|
||||
private fun collectGetField(getField: IrGetField) {
|
||||
getField.receiver?.let { collectRegions(it) }
|
||||
}
|
||||
|
||||
private fun collectSetField(setField: IrSetField) {
|
||||
collectRegions(setField.value)
|
||||
setField.receiver?.let { collectRegions(it) }
|
||||
}
|
||||
|
||||
private fun collectConst(const: IrConst<*>) {
|
||||
recordRegion(const)
|
||||
}
|
||||
|
||||
private fun collectReturn(irReturn: IrReturn) {
|
||||
collectRegions(irReturn.value)
|
||||
}
|
||||
|
||||
private fun collectWhen(irWhen: IrWhen) {
|
||||
irWhen.branches.forEach { branch ->
|
||||
// Do not record location for else branch since it doesn't look correct.
|
||||
if (branch.condition !is IrConst<*>) {
|
||||
collectRegions(branch.condition)
|
||||
if (it is IrElseBranch) {
|
||||
recordRegion(result)
|
||||
} else {
|
||||
recordRegion(condition)
|
||||
recordRegion(result, condition.endOffset, result.endOffset)
|
||||
condition.acceptChildrenVoid(this)
|
||||
}
|
||||
collectRegions(branch.result)
|
||||
result.acceptChildrenVoid(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectThrow(irThrow: IrThrow) {
|
||||
collectRegions(irThrow.value)
|
||||
recordRegion(irThrow)
|
||||
}
|
||||
override fun visitLoop(loop: IrLoop) {
|
||||
val condition = loop.condition
|
||||
recordRegion(condition)
|
||||
condition.acceptChildrenVoid(this)
|
||||
|
||||
private fun collectTry(irTry: IrTry) {
|
||||
}
|
||||
|
||||
private fun collectReturnableBlock(returnableBlock: IrReturnableBlock) {
|
||||
val file = (returnableBlock.sourceFileSymbol?.owner)
|
||||
if (file != null && file != currentFile && fileFilter(file)) {
|
||||
recordRegion(returnableBlock)
|
||||
irFileStack.push(file)
|
||||
returnableBlock.acceptChildrenVoid(this)
|
||||
irFileStack.pop()
|
||||
val body = loop.body ?: return
|
||||
when (loop) {
|
||||
is IrWhileLoop -> recordRegion(body, condition.endOffset, body.endOffset)
|
||||
is IrDoWhileLoop -> recordRegion(body, body.startOffset, condition.startOffset)
|
||||
}
|
||||
body.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
private fun collectContainerExpression(containerExpression: IrContainerExpression) {
|
||||
containerExpression.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
private fun collectWhileLoop(whileLoop: IrWhileLoop) {
|
||||
collectRegions(whileLoop.condition)
|
||||
whileLoop.body?.let { collectRegions(it) }
|
||||
}
|
||||
|
||||
private fun collectDoWhileLoop(doWhileLoop: IrDoWhileLoop) {
|
||||
collectRegions(doWhileLoop.condition)
|
||||
doWhileLoop.body?.let { collectRegions(it) }
|
||||
}
|
||||
|
||||
private fun collectVararg(vararg: IrVararg) {
|
||||
vararg.elements.forEach { it.acceptVoid(this) }
|
||||
}
|
||||
|
||||
private fun collectBreak(irBreak: IrBreak) {
|
||||
recordRegion(irBreak)
|
||||
}
|
||||
|
||||
private fun collectContinue(irContinue: IrContinue) {
|
||||
recordRegion(irContinue)
|
||||
}
|
||||
|
||||
private fun collectGetObjectValue(getObjectValue: IrGetObjectValue) {
|
||||
|
||||
}
|
||||
|
||||
private fun collectFunctionReference(functionReference: IrFunctionReference) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
private fun collectSuspendableExpression(suspendableExpression: IrSuspendableExpression) {
|
||||
|
||||
}
|
||||
|
||||
private fun collectSuspensionPoint(suspensionPoint: IrSuspensionPoint) {
|
||||
|
||||
}
|
||||
|
||||
private fun collectTypeOperator(typeOperatorCall: IrTypeOperatorCall) {
|
||||
|
||||
}
|
||||
|
||||
private fun collectCall(call: IrFunctionAccessExpression) {
|
||||
recordRegion(call, RegionKind.Code)
|
||||
call.acceptChildrenVoid(this)
|
||||
override fun visitBlock(expression: IrBlock) {
|
||||
when (expression) {
|
||||
is IrReturnableBlock -> {
|
||||
val file = (expression.sourceFileSymbol?.owner)
|
||||
if (file != null && file != currentFile && fileFilter(file)) {
|
||||
recordRegion(expression)
|
||||
irFileStack.push(file)
|
||||
expression.acceptChildrenVoid(this)
|
||||
irFileStack.pop()
|
||||
}
|
||||
}
|
||||
else -> expression.acceptChildrenVoid(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun recordRegion(irElement: IrElement, kind: RegionKind = RegionKind.Code) {
|
||||
if (irElement.startOffset == UNDEFINED_OFFSET || irElement.endOffset == UNDEFINED_OFFSET) {
|
||||
recordRegion(irElement, irElement.startOffset, irElement.endOffset, kind)
|
||||
}
|
||||
|
||||
private fun recordRegion(irElement: IrElement, startOffset: Int, endOffset: Int, kind: RegionKind = RegionKind.Code) {
|
||||
if (startOffset == UNDEFINED_OFFSET || endOffset == UNDEFINED_OFFSET) {
|
||||
return
|
||||
}
|
||||
regions[irElement] = Region.fromIr(irElement, currentFile, kind)
|
||||
regions[irElement] = Region.fromOffset(startOffset, endOffset, currentFile, kind)
|
||||
}
|
||||
}
|
||||
@@ -3936,6 +3936,21 @@ if (UtilsKt.testTargetSupportsCodeCoverage(project)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task coverage_controlflow(type: CoverageTest) {
|
||||
binaryName = "CoverageControlFlow"
|
||||
numberOfCoveredFunctions = 1
|
||||
numberOfCoveredLines = 102
|
||||
|
||||
konanArtifacts {
|
||||
program(binaryName, targets: [ target ]) {
|
||||
srcDir 'coverage/basic/controlflow'
|
||||
baseDir "$testOutputCoverage/$binaryName"
|
||||
entryPoint "coverage.basic.controlflow.main"
|
||||
extraOpts "-Xcoverage", "-Xcoverage-file=$profrawFile"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
package coverage.basic.controlflow
|
||||
|
||||
fun main() {
|
||||
|
||||
// If Expression
|
||||
|
||||
var a = 1
|
||||
var b = 2
|
||||
if (a < b) println("a < b")
|
||||
|
||||
if (a > b) {
|
||||
println("a > b")
|
||||
} else if (a == b) {
|
||||
println("a == b")
|
||||
} else {
|
||||
println("a < b")
|
||||
}
|
||||
|
||||
if (a < b) {
|
||||
println("a < b")
|
||||
}
|
||||
else
|
||||
{
|
||||
println("a >= b")
|
||||
}
|
||||
|
||||
var max = if (a > b) a else b
|
||||
|
||||
max = if (a > b) {
|
||||
println("Choose a")
|
||||
a
|
||||
} else {
|
||||
println("Choose b")
|
||||
b
|
||||
}
|
||||
|
||||
if (a < b)
|
||||
|
||||
println("a < b")
|
||||
else
|
||||
|
||||
println("a >= b")
|
||||
|
||||
if (a > b)
|
||||
|
||||
println("a > b")
|
||||
|
||||
else
|
||||
|
||||
println("a <= b")
|
||||
|
||||
// When Expression
|
||||
|
||||
when {
|
||||
a < b -> {
|
||||
println("a < b")
|
||||
}
|
||||
|
||||
a == b ->
|
||||
|
||||
println("a == b")
|
||||
a > b -> {
|
||||
println("a > b")
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var x = 1
|
||||
when (x) {
|
||||
1 -> print("x == 1")
|
||||
2 -> print("x == 2")
|
||||
else -> { // Note the block
|
||||
print("x is neither 1 nor 2")
|
||||
}
|
||||
}
|
||||
x = 2
|
||||
when (x) {
|
||||
1 -> print("x == 1")
|
||||
2 -> print("x == 2")
|
||||
else -> { // Note the block
|
||||
print("x is neither 1 nor 2")
|
||||
}
|
||||
}
|
||||
x = 3
|
||||
when (x) {
|
||||
1 -> print("x == 1")
|
||||
2 -> print("x == 2")
|
||||
else -> { // Note the block
|
||||
print("x is neither 1 nor 2")
|
||||
}
|
||||
}
|
||||
|
||||
when (x) {
|
||||
0, 1
|
||||
->
|
||||
print("x == 0 or x == 1")
|
||||
else ->
|
||||
print("otherwise")
|
||||
}
|
||||
|
||||
when {
|
||||
else -> println("=)")
|
||||
}
|
||||
|
||||
// While Loops
|
||||
|
||||
do {
|
||||
b++
|
||||
} while (b < 5)
|
||||
|
||||
while (a < 10) {
|
||||
a++
|
||||
if (a > 7) {
|
||||
println(a)
|
||||
}
|
||||
}
|
||||
|
||||
while (a > 0)
|
||||
|
||||
a--
|
||||
}
|
||||
Reference in New Issue
Block a user