Skip to content
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
5 changes: 0 additions & 5 deletions compiler/src/dotty/tools/dotc/coverage/Coverage.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@ class Coverage:
if stmt.id >= _nextStatementId then _nextStatementId = stmt.id + 1
statementsById(stmt.id) = stmt

def removeStatementsFromFile(sourcePath: Path | Null) =
val removedIds = statements.filter(_.location.sourcePath == sourcePath).map(_.id.toLong)
removedIds.foreach(statementsById.remove)


/**
* A statement that can be invoked, and thus counted as "covered" by code coverage tools.
*
Expand Down
46 changes: 25 additions & 21 deletions compiler/src/dotty/tools/dotc/transform/InstrumentCoverage.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package dotty.tools.dotc
package transform

import java.io.File
import java.nio.file.Files
import java.nio.file.{Files, Path}

import ast.tpd.*
import collection.mutable
Expand Down Expand Up @@ -42,54 +42,61 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:

private var coverageExcludeClasslikePatterns: List[Pattern] = Nil
private var coverageExcludeFilePatterns: List[Pattern] = Nil
private var lastCompiledFiles: Set[String] = Set.empty

override def run(using ctx: Context): Unit =
override def runOn(units: List[CompilationUnit])(using ctx: Context): List[CompilationUnit] =
val outputPath = ctx.settings.coverageOutputDir.value

// Ensure the dir exists
// Ensure the dir exists (once per batch, not per unit)
val dataDir = File(outputPath)
val newlyCreated = dataDir.mkdirs()

if !newlyCreated then
// If the directory existed before, clean measurement files.
dataDir.listFiles
.filter(_.getName.startsWith("scoverage.measurements."))
.foreach(_.delete())
val files = dataDir.listFiles
if files != null then
files
.filter(_.getName.startsWith("scoverage.measurements."))
.foreach(_.delete())
end if

// Deserialize previous coverage once at the start
val coverageFilePath = Serializer.coverageFilePath(outputPath)
val previousCoverage =
if Files.exists(coverageFilePath) then
Serializer.deserialize(coverageFilePath, ctx.settings.sourceroot.value)
else Coverage()

// Initialise a coverage object if it does not exist yet
if ctx.base.coverage == null then
ctx.base.coverage = Coverage()

// Initialize coverage patterns once
coverageExcludeClasslikePatterns = ctx.settings.coverageExcludeClasslikes.value.map(_.r.pattern)
coverageExcludeFilePatterns = ctx.settings.coverageExcludeFiles.value.map(_.r.pattern)

ctx.base.coverage.nn.removeStatementsFromFile(ctx.compilationUnit.source.file.absolute.jpath)
// Initialize coverage object
ctx.base.coverage = Coverage()
ctx.base.coverage.nn.setNextStatementId(previousCoverage.nextStatementId())

super.run
// Run the transformation on all units
val result = super.runOn(units)

// Serialize once at the end with merged coverage
val mergedCoverage = Coverage()
val currentFiles = units.map(_.source.file.absolute.jpath)

// Add statements from previous coverage that aren't from recompiled files
// and whose source files still exist
previousCoverage.statements
.filterNot(stmt =>
val source = stmt.location.sourcePath
lastCompiledFiles.contains(source.toString) || !Files.exists(source)
currentFiles.contains(source) || !Files.exists(source)
)
.foreach { stmt =>
mergedCoverage.addStatement(stmt)
}
ctx.base.coverage.nn.statements.foreach(stmt => mergedCoverage.addStatement(stmt))
.foreach(mergedCoverage.addStatement)

// Add all new statements from this compilation
ctx.base.coverage.nn.statements.foreach(mergedCoverage.addStatement)

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

result

private def isClassIncluded(sym: Symbol)(using Context): Boolean =
val fqn = sym.fullName.toText(ctx.printerFn(ctx)).show
coverageExcludeClasslikePatterns.isEmpty || !coverageExcludeClasslikePatterns.exists(
Expand Down Expand Up @@ -275,9 +282,6 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
InstrumentedParts.singleExprTree(coverageCall, transformed)

override def transform(tree: Tree)(using Context): Tree =
val path = tree.sourcePos.source.file.absolute.jpath
if path != null then lastCompiledFiles += path.toString

inContext(transformCtx(tree)) { // necessary to position inlined code properly
tree match
// simple cases
Expand Down
Loading