@@ -21,53 +21,54 @@ import dotty.tools.dotc.typer.LiftCoverage
21
21
22
22
import scala .quoted
23
23
24
- /** Phase that implements code coverage, executed when the "-coverage
25
- * OUTPUT_PATH" is added to the compilation.
26
- */
27
- class CoverageTransformMacro extends MacroTransform with IdentityDenotTransformer {
24
+ /** Implements code coverage by inserting calls to scala.runtime.Invoker
25
+ * ("instruments" the source code).
26
+ * The result can then be consumed by the Scoverage tool.
27
+ */
28
+ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer :
28
29
import ast .tpd ._
29
30
30
- override def phaseName = " coverage "
31
+ override def phaseName = InstrumentCoverage .name
31
32
32
- // Atomic counter used for assignation of IDs to difference statements
33
- val statementId = new AtomicInteger (0 )
33
+ override def description = InstrumentCoverage .description
34
34
35
- var outputPath = " "
35
+ // Enabled by argument "-coverage OUTPUT_DIR"
36
+ override def isEnabled (using ctx : Context ) =
37
+ ctx.settings.coverageOutputDir.value.nonEmpty
36
38
37
- // Main class used to store all instrumented statements
38
- val coverage = new Coverage
39
+ // Atomic counter used for assignation of IDs to difference statements
40
+ private val statementId = AtomicInteger ( 0 )
39
41
40
- override def run ( using ctx : Context ) : Unit = {
42
+ private var outputPath = " "
41
43
42
- if (ctx.settings.coverageOutputDir.value.nonEmpty) {
43
- outputPath = ctx.settings.coverageOutputDir.value
44
+ // Main class used to store all instrumented statements
45
+ private val coverage = Coverage ()
44
46
45
- // Ensure the dir exists
46
- val dataDir = new File (outputPath)
47
- val newlyCreated = dataDir.mkdirs()
47
+ override def run (using ctx : Context ): Unit =
48
+ outputPath = ctx.settings.coverageOutputDir.value
48
49
49
- if (! newlyCreated) {
50
- // If the directory existed before, let's clean it up.
51
- dataDir.listFiles
52
- .filter(_.getName.startsWith(" scoverage" ))
53
- .foreach(_.delete)
54
- }
50
+ // Ensure the dir exists
51
+ val dataDir = new File (outputPath)
52
+ val newlyCreated = dataDir.mkdirs()
55
53
56
- super .run
54
+ if (! newlyCreated) {
55
+ // If the directory existed before, let's clean it up.
56
+ dataDir.listFiles
57
+ .filter(_.getName.startsWith(" scoverage" ))
58
+ .foreach(_.delete)
59
+ }
57
60
61
+ super .run
58
62
59
- Serializer .serialize(coverage, outputPath, ctx.settings.coverageSourceroot.value)
60
- }
61
- }
63
+ Serializer .serialize(coverage, outputPath, ctx.settings.coverageSourceroot.value)
62
64
63
- protected def newTransformer (using Context ): Transformer =
64
- new CoverageTransormer
65
+ override protected def newTransformer (using Context ) = CoverageTransormer ()
65
66
66
- class CoverageTransormer extends Transformer {
67
+ class CoverageTransormer extends Transformer :
67
68
var instrumented = false
68
69
69
- override def transform (tree : Tree )(using Context ): Tree = {
70
- tree match {
70
+ override def transform (tree : Tree )(using Context ): Tree =
71
+ tree match
71
72
case tree : If =>
72
73
cpy.If (tree)(
73
74
cond = transform(tree.cond),
@@ -139,10 +140,8 @@ class CoverageTransformMacro extends MacroTransform with IdentityDenotTransforme
139
140
tree.sourcePos
140
141
)
141
142
super .transform(tree)
142
- }
143
- }
144
143
145
- def liftApply (tree : Apply )(using Context ) = {
144
+ def liftApply (tree : Apply )(using Context ) =
146
145
val buffer = mutable.ListBuffer [Tree ]()
147
146
// NOTE: that if only one arg needs to be lifted, we just lift everything
148
147
val lifted = LiftCoverage .liftForCoverage(buffer, tree)
@@ -156,22 +155,18 @@ class CoverageTransformMacro extends MacroTransform with IdentityDenotTransforme
156
155
false
157
156
)
158
157
)
159
- }
160
158
161
- def instrumentCasees (cases : List [CaseDef ])(using Context ): List [CaseDef ] = {
159
+ def instrumentCasees (cases : List [CaseDef ])(using Context ): List [CaseDef ] =
162
160
cases.map(instrumentCaseDef)
163
- }
164
161
165
- def instrumentCaseDef (tree : CaseDef )(using Context ): CaseDef = {
162
+ def instrumentCaseDef (tree : CaseDef )(using Context ): CaseDef =
166
163
cpy.CaseDef (tree)(tree.pat, transform(tree.guard), transform(tree.body))
167
- }
168
164
169
- def instrument (tree : Tree , branch : Boolean = false )(using Context ): Tree = {
165
+ def instrument (tree : Tree , branch : Boolean = false )(using Context ): Tree =
170
166
instrument(tree, tree.sourcePos, branch)
171
- }
172
167
173
- def instrument (tree : Tree , pos : SourcePosition , branch : Boolean )(using ctx : Context ): Tree = {
174
- if (pos.exists && ! pos.span.isZeroExtent && ! tree.isType) {
168
+ def instrument (tree : Tree , pos : SourcePosition , branch : Boolean )(using ctx : Context ): Tree =
169
+ if (pos.exists && ! pos.span.isZeroExtent && ! tree.isType)
175
170
val id = statementId.incrementAndGet()
176
171
val statement = new Statement (
177
172
source = ctx.source.file.name,
@@ -187,18 +182,16 @@ class CoverageTransformMacro extends MacroTransform with IdentityDenotTransforme
187
182
)
188
183
coverage.addStatement(statement)
189
184
Block (List (invokeCall(id)), tree)
190
- } else {
185
+ else
191
186
tree
192
- }
193
- }
194
187
195
- def invokeCall (id : Int )(using Context ): Tree = {
188
+ def invokeCall (id : Int )(using Context ): Tree =
196
189
ref(defn.InvokerModuleRef )
197
190
.select(" invoked" .toTermName)
198
191
.appliedToArgs(
199
192
List (Literal (Constant (id)), Literal (Constant (outputPath)))
200
193
)
201
- }
202
- }
203
194
204
- }
195
+ object InstrumentCoverage :
196
+ val name : String = " instrumentCoverage"
197
+ val description : String = " instrument code for coverage cheking"
0 commit comments