Skip to content

Commit

Permalink
Port coverage filter options for packages and files (#19727)
Browse files Browse the repository at this point in the history
- Add `-coverage-exclude-packages` option that excludes packages and
classes
  from generating coverage
- Add `-coverage-exclude-files` option that excludes files from
  generating coverage
  • Loading branch information
KacperFKorban authored Feb 27, 2024
2 parents 7f410aa + 641f5db commit 18be8d1
Show file tree
Hide file tree
Showing 13 changed files with 251 additions and 6 deletions.
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
# ------------------------------------------

0 comments on commit 18be8d1

Please sign in to comment.