[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.util.nameForIrSerialization
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collect all regions in the module.
|
* Collect all regions in the module.
|
||||||
@@ -84,169 +83,63 @@ private class IrFunctionRegionsCollector(
|
|||||||
override fun visitFunction(declaration: IrFunction) {
|
override fun visitFunction(declaration: IrFunction) {
|
||||||
declaration.body?.let {
|
declaration.body?.let {
|
||||||
recordRegion(it)
|
recordRegion(it)
|
||||||
visitBody(it)
|
it.acceptChildrenVoid(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitExpression(expression: IrExpression) {
|
override fun visitWhen(expression: IrWhen) {
|
||||||
collectRegions(expression)
|
val branches = expression.branches
|
||||||
}
|
branches.forEach {
|
||||||
|
val condition = it.condition
|
||||||
|
val result = it.result
|
||||||
|
|
||||||
override fun visitVariable(declaration: IrVariable) {
|
if (it is IrElseBranch) {
|
||||||
recordRegion(declaration)
|
recordRegion(result)
|
||||||
declaration.initializer?.let { collectRegions(it) }
|
} else {
|
||||||
}
|
recordRegion(condition)
|
||||||
|
recordRegion(result, condition.endOffset, result.endOffset)
|
||||||
override fun visitBody(body: IrBody) = when (body) {
|
condition.acceptChildrenVoid(this)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
collectRegions(branch.result)
|
result.acceptChildrenVoid(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collectThrow(irThrow: IrThrow) {
|
override fun visitLoop(loop: IrLoop) {
|
||||||
collectRegions(irThrow.value)
|
val condition = loop.condition
|
||||||
recordRegion(irThrow)
|
recordRegion(condition)
|
||||||
}
|
condition.acceptChildrenVoid(this)
|
||||||
|
|
||||||
private fun collectTry(irTry: IrTry) {
|
val body = loop.body ?: return
|
||||||
}
|
when (loop) {
|
||||||
|
is IrWhileLoop -> recordRegion(body, condition.endOffset, body.endOffset)
|
||||||
private fun collectReturnableBlock(returnableBlock: IrReturnableBlock) {
|
is IrDoWhileLoop -> recordRegion(body, body.startOffset, condition.startOffset)
|
||||||
val file = (returnableBlock.sourceFileSymbol?.owner)
|
|
||||||
if (file != null && file != currentFile && fileFilter(file)) {
|
|
||||||
recordRegion(returnableBlock)
|
|
||||||
irFileStack.push(file)
|
|
||||||
returnableBlock.acceptChildrenVoid(this)
|
|
||||||
irFileStack.pop()
|
|
||||||
}
|
}
|
||||||
|
body.acceptChildrenVoid(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collectContainerExpression(containerExpression: IrContainerExpression) {
|
override fun visitBlock(expression: IrBlock) {
|
||||||
containerExpression.acceptChildrenVoid(this)
|
when (expression) {
|
||||||
}
|
is IrReturnableBlock -> {
|
||||||
|
val file = (expression.sourceFileSymbol?.owner)
|
||||||
private fun collectWhileLoop(whileLoop: IrWhileLoop) {
|
if (file != null && file != currentFile && fileFilter(file)) {
|
||||||
collectRegions(whileLoop.condition)
|
recordRegion(expression)
|
||||||
whileLoop.body?.let { collectRegions(it) }
|
irFileStack.push(file)
|
||||||
}
|
expression.acceptChildrenVoid(this)
|
||||||
|
irFileStack.pop()
|
||||||
private fun collectDoWhileLoop(doWhileLoop: IrDoWhileLoop) {
|
}
|
||||||
collectRegions(doWhileLoop.condition)
|
}
|
||||||
doWhileLoop.body?.let { collectRegions(it) }
|
else -> expression.acceptChildrenVoid(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun recordRegion(irElement: IrElement, kind: RegionKind = RegionKind.Code) {
|
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
|
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