diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/CoverageRegionCollector.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/CoverageRegionCollector.kt index b599f11e032..8d0b9e13007 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/CoverageRegionCollector.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/CoverageRegionCollector.kt @@ -11,8 +11,10 @@ import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.util.nameForIrSerialization +import org.jetbrains.kotlin.ir.util.statements 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. @@ -40,7 +42,7 @@ internal class CoverageRegionCollector(private val fileFilter: (IrFile) -> Boole override fun visitFunction(declaration: IrFunction) { if (!declaration.isInline && !declaration.isExternal && !declaration.isGeneratedByCompiler) { val regionsCollector = IrFunctionRegionsCollector(fileFilter, file) - regionsCollector.visitFunction(declaration) + declaration.acceptVoid(regionsCollector) if (regionsCollector.regions.isNotEmpty()) { functionRegions += FunctionRegions(declaration, regionsCollector.regions) } @@ -80,13 +82,30 @@ private class IrFunctionRegionsCollector( element.acceptChildrenVoid(this) } - override fun visitFunction(declaration: IrFunction) { + override fun visitSimpleFunction(declaration: IrSimpleFunction) { declaration.body?.let { recordRegion(it) it.acceptChildrenVoid(this) } } + override fun visitConstructor(declaration: IrConstructor) { + val statements = declaration.body?.statements ?: return + statements.forEach { + if (it is IrDelegatingConstructorCall && !declaration.isPrimary + || it !is IrDelegatingConstructorCall && it !is IrReturn) { + recordRegion(it) + it.acceptVoid(this) + } + } + } + + // TODO: The following implementation produces correct region mapping, but something goes wrong later + // override fun visitFieldAccess(expression: IrFieldAccessExpression) { + // expression.receiver?.let { recordRegion(it) } + // expression.acceptChildrenVoid(this) + // } + override fun visitWhen(expression: IrWhen) { val branches = expression.branches branches.forEach { diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 34b5dadce2d..b9934826d33 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -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" + } + } + } + task coverage_smoke0(type: CoverageTest) { binaryName = "CoverageSmoke0" numberOfCoveredFunctions = 2 @@ -3951,16 +3966,16 @@ if (UtilsKt.testTargetSupportsCodeCoverage(project)) { } } - task coverage_controlflow(type: CoverageTest) { - binaryName = "CoverageControlFlow" - numberOfCoveredFunctions = 1 - numberOfCoveredLines = 102 + task coverage_smoke1(type: CoverageTest) { + binaryName = "CoverageSmoke1" + numberOfCoveredFunctions = 9 + numberOfCoveredLines = 33 konanArtifacts { program(binaryName, targets: [ target ]) { - srcDir 'coverage/basic/controlflow' + srcDir 'coverage/basic/smoke1' baseDir "$testOutputCoverage/$binaryName" - entryPoint "coverage.basic.controlflow.main" + entryPoint "coverage.basic.smoke1.main" extraOpts "-Xcoverage", "-Xcoverage-file=$profrawFile" } } diff --git a/backend.native/tests/coverage/basic/smoke1/smoke1.kt b/backend.native/tests/coverage/basic/smoke1/smoke1.kt new file mode 100644 index 00000000000..c7302e5eaf4 --- /dev/null +++ b/backend.native/tests/coverage/basic/smoke1/smoke1.kt @@ -0,0 +1,48 @@ +package coverage.basic.smoke1 + +class A(val prop: Int) { + + constructor() : this(1) + + fun action1() { + println(prop) + } + + fun action2() { + + } +} + +class B private constructor(val prop: String) { + + init { + println("init block") + } + + constructor(prop1: String, prop2: String) : this("$prop1, $prop2") + + constructor() : this("dummy") { + if (1 > 2) { + println("uncovered") + } else { + println("foo") + } + println("bar") + } + + override fun toString() = prop +} + +fun main(args: Array) { + val a1 = A(2) + a1.action1() + a1.action2() + val a2 = A() + a2.action1() + a2.action1() + + val b1 = B("Hello", "world") + val b2 = B() + println(b1) + println(b2) +}