Skip to content

Port coverage filter options for packages and files #19727

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,3 @@ docs/_spec/.jekyll-metadata

# scaladoc related
scaladoc/output/

#coverage
coverage/

2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ trait CommonScalaSettings:

/* Coverage settings */
val coverageOutputDir = PathSetting("-coverage-out", "Destination for coverage classfiles and instrumentation data.", "", aliases = List("--coverage-out"))
val coverageExcludeClasslikes: Setting[List[String]] = MultiStringSetting("-coverage-exclude-classlikes", "packages, classes and modules", "List of regexes for packages, classes and modules to exclude from coverage.", aliases = List("--coverage-exclude-classlikes"))
val coverageExcludeFiles: Setting[List[String]] = MultiStringSetting("-coverage-exclude-files", "files", "List of regexes for files to exclude from coverage.", aliases = List("--coverage-exclude-files"))

/* Other settings */
val encoding: Setting[String] = StringSetting("-encoding", "encoding", "Specify character encoding used by source files.", Properties.sourceEncoding, aliases = List("--encoding"))
Expand Down
35 changes: 33 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/InstrumentCoverage.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ import core.Constants.Constant
import core.NameOps.isContextFunction
import core.StdNames.nme
import core.Types.*
import core.Decorators.*
import coverage.*
import typer.LiftCoverage
import util.{SourcePosition, SourceFile}
import util.Spans.Span
import localopt.StringInterpolatorOpt
import inlines.Inlines
import scala.util.matching.Regex
import java.util.regex.Pattern

/** Implements code coverage by inserting calls to scala.runtime.coverage.Invoker
* ("instruments" the source code).
Expand All @@ -41,6 +44,9 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
// stores all instrumented statements
private val coverage = Coverage()

private var coverageExcludeClasslikePatterns: List[Pattern] = Nil
private var coverageExcludeFilePatterns: List[Pattern] = Nil

override def run(using ctx: Context): Unit =
val outputPath = ctx.settings.coverageOutputDir.value

Expand All @@ -54,10 +60,26 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
.filter(_.nn.getName.nn.startsWith("scoverage"))
.foreach(_.nn.delete())
end if

coverageExcludeClasslikePatterns = ctx.settings.coverageExcludeClasslikes.value.map(_.r.pattern)
coverageExcludeFilePatterns = ctx.settings.coverageExcludeFiles.value.map(_.r.pattern)

super.run

Serializer.serialize(coverage, outputPath, ctx.settings.sourceroot.value)

private def isClassIncluded(sym: Symbol)(using Context): Boolean =
val fqn = sym.fullName.toText(ctx.printerFn(ctx)).show
coverageExcludeClasslikePatterns.isEmpty || !coverageExcludeClasslikePatterns.exists(
_.matcher(fqn).nn.matches
)

private def isFileIncluded(file: SourceFile)(using Context): Boolean =
val normalizedPath = file.path.replace(".scala", "")
coverageExcludeFilePatterns.isEmpty || !coverageExcludeFilePatterns.exists(
_.matcher(normalizedPath).nn.matches
)

override protected def newTransformer(using Context) =
CoverageTransformer(ctx.settings.coverageOutputDir.value)

Expand Down Expand Up @@ -269,8 +291,17 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
transformDefDef(tree)

case tree: PackageDef =>
// only transform the statements of the package
cpy.PackageDef(tree)(tree.pid, transform(tree.stats))
if isFileIncluded(tree.srcPos.sourcePos.source) && isClassIncluded(tree.symbol) then
// only transform the statements of the package
cpy.PackageDef(tree)(tree.pid, transform(tree.stats))
else
tree

case tree: TypeDef =>
if isFileIncluded(tree.srcPos.sourcePos.source) && isClassIncluded(tree.symbol) then
super.transform(tree)
else
tree

case tree: Assign =>
// only transform the rhs
Expand Down
19 changes: 19 additions & 0 deletions tests/coverage/pos/ExcludeClass.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//> using options -coverage-exclude-classlikes:covtest.Klass

package covtest

class Klass {
def abs(i: Int) =
if i > 0 then
i
else
-i
}

class Klass2 {
def abs(i: Int) =
if i > 0 then
i
else
-i
}
71 changes: 71 additions & 0 deletions tests/coverage/pos/ExcludeClass.scoverage.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Coverage data, format version: 3.0
# Statement data:
# - id
# - source path
# - package name
# - class name
# - class type (Class, Object or Trait)
# - full class name
# - method name
# - start offset
# - end offset
# - line number
# - symbol name
# - tree name
# - is branch
# - invocations count
# - is ignored
# - description (can be multi-line)
# ' ' sign
# ------------------------------------------
0
ExcludeClass.scala
covtest
Klass2
Class
covtest.Klass2
abs
219
220
16
i
Ident
true
0
false
i

1
ExcludeClass.scala
covtest
Klass2
Class
covtest.Klass2
abs
236
238
18
unary_-
Select
true
0
false
-i

2
ExcludeClass.scala
covtest
Klass2
Class
covtest.Klass2
abs
177
184
14
abs
DefDef
false
0
false
def abs

9 changes: 9 additions & 0 deletions tests/coverage/pos/ExcludeDef.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//> using options -coverage-exclude-classlikes:covtest\..*

package covtest

def abs(i: Int) =
if i > 0 then
i
else
-i
20 changes: 20 additions & 0 deletions tests/coverage/pos/ExcludeDef.scoverage.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Coverage data, format version: 3.0
# Statement data:
# - id
# - source path
# - package name
# - class name
# - class type (Class, Object or Trait)
# - full class name
# - method name
# - start offset
# - end offset
# - line number
# - symbol name
# - tree name
# - is branch
# - invocations count
# - is ignored
# - description (can be multi-line)
# ' ' sign
# ------------------------------------------
11 changes: 11 additions & 0 deletions tests/coverage/pos/ExcludeFile.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//> using options -coverage-exclude-files:.*ExcludeFile

package covtest

class Klass {
def abs(i: Int) =
if i > 0 then
i
else
-i
}
20 changes: 20 additions & 0 deletions tests/coverage/pos/ExcludeFile.scoverage.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Coverage data, format version: 3.0
# Statement data:
# - id
# - source path
# - package name
# - class name
# - class type (Class, Object or Trait)
# - full class name
# - method name
# - start offset
# - end offset
# - line number
# - symbol name
# - tree name
# - is branch
# - invocations count
# - is ignored
# - description (can be multi-line)
# ' ' sign
# ------------------------------------------
15 changes: 15 additions & 0 deletions tests/coverage/pos/ExcludeOtherStuff.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//> using options -coverage-exclude-classlikes:covtest.Oject,covtest.Tait

package covtest

object Oject {
def abs(i: Int) =
if i > 0 then
i
else
-i
}

trait Tait {
def abs(i: Int): Int
}
20 changes: 20 additions & 0 deletions tests/coverage/pos/ExcludeOtherStuff.scoverage.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Coverage data, format version: 3.0
# Statement data:
# - id
# - source path
# - package name
# - class name
# - class type (Class, Object or Trait)
# - full class name
# - method name
# - start offset
# - end offset
# - line number
# - symbol name
# - tree name
# - is branch
# - invocations count
# - is ignored
# - description (can be multi-line)
# ' ' sign
# ------------------------------------------
11 changes: 11 additions & 0 deletions tests/coverage/pos/ExcludePackage.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//> using options -coverage-exclude-classlikes:covtest

package covtest

class Klass {
def abs(i: Int) =
if i > 0 then
i
else
-i
}
20 changes: 20 additions & 0 deletions tests/coverage/pos/ExcludePackage.scoverage.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Coverage data, format version: 3.0
# Statement data:
# - id
# - source path
# - package name
# - class name
# - class type (Class, Object or Trait)
# - full class name
# - method name
# - start offset
# - end offset
# - line number
# - symbol name
# - tree name
# - is branch
# - invocations count
# - is ignored
# - description (can be multi-line)
# ' ' sign
# ------------------------------------------