[Coverage] Support conditional expressions

This commit is contained in:
Anastasiya Shadrina
2019-11-24 01:32:00 +07:00
committed by Sergey Bogolepov
parent 45a6c58d56
commit 70a7b61309
3 changed files with 182 additions and 148 deletions
@@ -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)
}
}
+15
View File
@@ -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--
}