Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a0588cd

Browse files
committedMar 5, 2023
fix: ignore lazy definition generated from by-name implicits
Preamble: by-name implicit parameters used for shapeless magic and were introduced in SIP-31: https://docs.scala-lang.org/sips/byname-implicits.html Using scoverage with such code may result in the following warning from the plugin: [warn] Could not instrument [Select/value rec$1]. Originally discovered by using scoverage with the code that derives typeclass instances using kittens library: case class Bar() case class Foo(bars: List[Bar]) object Foo { implicit eq: cats.Eq[Foo] = cats.derived.semiauto.eq } Solution: Ignore LazyDefns$1.rec$1 from instrumentation like other synthetic code Other changes: It was necessary to disable position validation phase for the test case that reproduces the issue because the code generated by scala compiler fails such validation. It could be also checked outside testing environment by manually setting -Yvalidate-pos:typer option to the compiler. Not sure whether it's expected compiler behavior or not
1 parent 6c3cd69 commit a0588cd

File tree

3 files changed

+46
-4
lines changed

3 files changed

+46
-4
lines changed
 

‎plugin/src/main/scala/scoverage/ScoveragePlugin.scala

+8
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,14 @@ class ScoverageInstrumentationComponent(
790790
*/
791791
case s: Select if s.symbol.isLazy => tree
792792

793+
// Generated by compiler for lazy definitions involving
794+
// by-name implicit parameters. More on that here:
795+
// https://docs.scala-lang.org/sips/byname-implicits.html
796+
//
797+
// final <synthetic> val lazyDefns$1: LazyDefns$1 = new LazyDefns$1();
798+
// lazyDefns$1.rec$1()
799+
case s: Select if s.symbol.isSynthetic => tree
800+
793801
case s: Select =>
794802
instrument(
795803
treeCopy.Select(s, traverseApplication(s.qualifier), s.name),

‎plugin/src/test/scala/scoverage/PluginCoverageTest.scala

+27
Original file line numberDiff line numberDiff line change
@@ -373,4 +373,31 @@ class PluginCoverageTest extends FunSuite with MacroSupport {
373373
assert(!compiler.reporter.hasWarnings)
374374
compiler.assertNMeasuredStatements(11)
375375
}
376+
377+
test(
378+
"scoverage should ignore synthetic lazy definitions generated by compiler from by-name implicits"
379+
) {
380+
val compiler = ScoverageCompiler.noPositionValidation
381+
compiler.compileCodeSnippet(
382+
"""
383+
|object test {
384+
|
385+
| trait Foo {
386+
| def next: Foo
387+
| }
388+
|
389+
| object Foo {
390+
| implicit def foo(implicit rec: => Foo): Foo =
391+
| new Foo { def next = rec }
392+
| }
393+
|
394+
| val foo = implicitly[Foo]
395+
|
396+
|}
397+
|
398+
""".stripMargin
399+
)
400+
assert(!compiler.reporter.hasErrors)
401+
assert(!compiler.reporter.hasWarnings)
402+
}
376403
}

‎plugin/src/test/scala/scoverage/ScoverageCompiler.scala

+11-4
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,17 @@ private[scoverage] object ScoverageCompiler {
5757

5858
def default: ScoverageCompiler = {
5959
val reporter = new scala.tools.nsc.reporters.ConsoleReporter(settings)
60-
new ScoverageCompiler(settings, reporter)
60+
new ScoverageCompiler(settings, reporter, validatePositions = true)
61+
}
62+
63+
def noPositionValidation: ScoverageCompiler = {
64+
val reporter = new scala.tools.nsc.reporters.ConsoleReporter(settings)
65+
new ScoverageCompiler(settings, reporter, validatePositions = false)
6166
}
6267

6368
def defaultJS: ScoverageCompiler = {
6469
val reporter = new scala.tools.nsc.reporters.ConsoleReporter(jsSettings)
65-
new ScoverageCompiler(jsSettings, reporter)
70+
new ScoverageCompiler(jsSettings, reporter, validatePositions = true)
6671
}
6772

6873
def locationCompiler: LocationCompiler = {
@@ -152,7 +157,8 @@ private[scoverage] object ScoverageCompiler {
152157

153158
class ScoverageCompiler(
154159
settings: scala.tools.nsc.Settings,
155-
rep: scala.tools.nsc.reporters.Reporter
160+
rep: scala.tools.nsc.reporters.Reporter,
161+
validatePositions: Boolean
156162
) extends scala.tools.nsc.Global(settings, rep) {
157163

158164
def addToClassPath(file: File): Unit = {
@@ -268,7 +274,8 @@ class ScoverageCompiler(
268274

269275
override def computeInternalPhases(): Unit = {
270276
super.computeInternalPhases()
271-
addToPhasesSet(validator, "scoverage validator")
277+
if (validatePositions)
278+
addToPhasesSet(validator, "scoverage validator")
272279
addToPhasesSet(
273280
instrumentationComponent,
274281
"scoverage instrumentationComponent"

0 commit comments

Comments
 (0)
Please sign in to comment.