From fe0c08090298e0664f5c9dea243352837f04e889 Mon Sep 17 00:00:00 2001 From: pkiatkraipob Date: Sat, 1 Nov 2025 16:23:07 +0700 Subject: [PATCH 1/2] bugfix: scoverage does not instrument pat-mat assignment properly --- .../scala/scoverage/ScoveragePlugin.scala | 14 ++++++++++ .../scala/scoverage/PluginCoverageTest.scala | 28 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/plugin/src/main/scala/scoverage/ScoveragePlugin.scala b/plugin/src/main/scala/scoverage/ScoveragePlugin.scala index ca3064f1..5f2af4b5 100644 --- a/plugin/src/main/scala/scoverage/ScoveragePlugin.scala +++ b/plugin/src/main/scala/scoverage/ScoveragePlugin.scala @@ -842,6 +842,20 @@ class ScoverageInstrumentationComponent( */ case v: ValDef if v.symbol.isLazy => tree + /** Pattern matching assignment instrumentation (see https://github.com/scoverage/scalac-scoverage-plugin/issues/123) + * + * User code: val (a, b) = { if (c) 1 -> 1 else 2 -> 2 } + * + * After typer, this desugars to: + * val x$1 = (if (c) 1 -> 1 else 2 -> 2) match { case (a, b) => Tuple2(a, b) } + * val a = x$1._1 + * val b = x$1._2 + * + * This will instrument the user expression (if-else with arrow calls). + */ + case v: ValDef if v.symbol.isSynthetic && v.rhs.pos.isDefined && containsNonSynthetic(v.rhs) => + treeCopy.ValDef(tree, v.mods, v.name, v.tpt, process(v.rhs)) + /** val default: A1 => B1 = * val x1: Any = _ */ diff --git a/plugin/src/test/scala/scoverage/PluginCoverageTest.scala b/plugin/src/test/scala/scoverage/PluginCoverageTest.scala index 15a85146..569b24b2 100644 --- a/plugin/src/test/scala/scoverage/PluginCoverageTest.scala +++ b/plugin/src/test/scala/scoverage/PluginCoverageTest.scala @@ -380,4 +380,32 @@ class PluginCoverageTest extends FunSuite with MacroSupport { assert(!compiler.reporter.hasWarnings) compiler.assertNMeasuredStatements(11) } + + test("plugin should handle return pattern matching assignment https://github.com/scoverage/scalac-scoverage-plugin/issues/123") { + val compiler = ScoverageCompiler.default + compiler.compileCodeSnippet( + """ + |object TestObject { + | def test(c: Boolean): Unit = { + | val (a, b) = { + | if (c) 1 -> 1 else 2 -> 2 + | } + | } + |} + """.stripMargin + ) + assert(!compiler.reporter.hasErrors) + assert(!compiler.reporter.hasWarnings) + /** + * WITHOUT the bugfix: + * 2 when assigning value to "a" and "b" + * 1 at the end of the function + * WITH the bugfix, it will additionally include + * 2 from then branch + * 2 from else branch + * 2 from synthetic code generated for pattern matching assignment + */ + compiler.assertNMeasuredStatements(9) + } + } From ec610f1b1374e7ee6a76c8175090524214481776 Mon Sep 17 00:00:00 2001 From: pkiatkraipob Date: Mon, 3 Nov 2025 17:20:08 +0700 Subject: [PATCH 2/2] style: fix scalafmt error --- .../scala/scoverage/ScoveragePlugin.scala | 5 ++++- .../scala/scoverage/PluginCoverageTest.scala | 22 ++++++++++--------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/plugin/src/main/scala/scoverage/ScoveragePlugin.scala b/plugin/src/main/scala/scoverage/ScoveragePlugin.scala index 5f2af4b5..bedc079d 100644 --- a/plugin/src/main/scala/scoverage/ScoveragePlugin.scala +++ b/plugin/src/main/scala/scoverage/ScoveragePlugin.scala @@ -853,7 +853,10 @@ class ScoverageInstrumentationComponent( * * This will instrument the user expression (if-else with arrow calls). */ - case v: ValDef if v.symbol.isSynthetic && v.rhs.pos.isDefined && containsNonSynthetic(v.rhs) => + case v: ValDef + if v.symbol.isSynthetic && v.rhs.pos.isDefined && containsNonSynthetic( + v.rhs + ) => treeCopy.ValDef(tree, v.mods, v.name, v.tpt, process(v.rhs)) /** val default: A1 => B1 = diff --git a/plugin/src/test/scala/scoverage/PluginCoverageTest.scala b/plugin/src/test/scala/scoverage/PluginCoverageTest.scala index 569b24b2..eab838f4 100644 --- a/plugin/src/test/scala/scoverage/PluginCoverageTest.scala +++ b/plugin/src/test/scala/scoverage/PluginCoverageTest.scala @@ -381,7 +381,9 @@ class PluginCoverageTest extends FunSuite with MacroSupport { compiler.assertNMeasuredStatements(11) } - test("plugin should handle return pattern matching assignment https://github.com/scoverage/scalac-scoverage-plugin/issues/123") { + test( + "plugin should handle return pattern matching assignment https://github.com/scoverage/scalac-scoverage-plugin/issues/123" + ) { val compiler = ScoverageCompiler.default compiler.compileCodeSnippet( """ @@ -396,15 +398,15 @@ class PluginCoverageTest extends FunSuite with MacroSupport { ) assert(!compiler.reporter.hasErrors) assert(!compiler.reporter.hasWarnings) - /** - * WITHOUT the bugfix: - * 2 when assigning value to "a" and "b" - * 1 at the end of the function - * WITH the bugfix, it will additionally include - * 2 from then branch - * 2 from else branch - * 2 from synthetic code generated for pattern matching assignment - */ + + /** WITHOUT the bugfix: + * 2 when assigning value to "a" and "b" + * 1 at the end of the function + * WITH the bugfix, it will additionally include + * 2 from then branch + * 2 from else branch + * 2 from synthetic code generated for pattern matching assignment + */ compiler.assertNMeasuredStatements(9) }