@@ -145,6 +145,29 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
145
145
val span = pos.span.toSynthetic
146
146
invokeCall(statementId, span)
147
147
148
+ private def transformApplyArgs (trees : List [Tree ])(using Context ): List [Tree ] =
149
+ if (allConstArgs(trees)) trees else transform(trees)
150
+
151
+ private def transformInnerApply (tree : Tree )(using Context ): Tree = tree match
152
+ case a : Apply if a.fun.symbol == defn.StringContextModule_apply =>
153
+ a
154
+ case a : Apply =>
155
+ cpy.Apply (a)(
156
+ transformInnerApply(a.fun),
157
+ transformApplyArgs(a.args)
158
+ )
159
+ case a : TypeApply =>
160
+ cpy.TypeApply (a)(
161
+ transformInnerApply(a.fun),
162
+ transformApplyArgs(a.args)
163
+ )
164
+ case s : Select =>
165
+ cpy.Select (s)(transformInnerApply(s.qualifier), s.name)
166
+ case i : (Ident | This ) => i
167
+ case other => transform(other)
168
+
169
+ private def allConstArgs (args : List [Tree ]) =
170
+ args.forall(arg => arg.isInstanceOf [Literal ] || arg.isInstanceOf [Ident ])
148
171
/**
149
172
* Tries to instrument an `Apply`.
150
173
* These "tryInstrument" methods are useful to tweak the generation of coverage instrumentation,
@@ -158,10 +181,12 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
158
181
// Create a call to Invoker.invoked(coverageDirectory, newStatementId)
159
182
val coverageCall = createInvokeCall(tree, tree.sourcePos)
160
183
161
- if needsLift(tree) then
162
- // Transform args and fun, i.e. instrument them if needed (and if possible)
163
- val app = cpy.Apply (tree)(transform(tree.fun), tree.args.map(transform))
184
+ // Transform args and fun, i.e. instrument them if needed (and if possible)
185
+ val app =
186
+ if (tree.fun.symbol eq defn.throwMethod) tree
187
+ else cpy.Apply (tree)(transformInnerApply(tree.fun), transformApplyArgs(tree.args))
164
188
189
+ if needsLift(tree) then
165
190
// Lifts the arguments. Note that if only one argument needs to be lifted, we lift them all.
166
191
// Also, tree.fun can be lifted too.
167
192
// See LiftCoverage for the internal working of this lifting.
@@ -171,11 +196,10 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
171
196
InstrumentedParts (liftedDefs.toList, coverageCall, liftedApp)
172
197
else
173
198
// Instrument without lifting
174
- val transformed = cpy.Apply (tree)(transform(tree.fun), transform(tree.args))
175
- InstrumentedParts .singleExpr(coverageCall, transformed)
199
+ InstrumentedParts .singleExpr(coverageCall, app)
176
200
else
177
201
// Transform recursively but don't instrument the tree itself
178
- val transformed = cpy.Apply (tree)(transform (tree.fun), transform(tree.args))
202
+ val transformed = cpy.Apply (tree)(transformInnerApply (tree.fun), transform(tree.args))
179
203
InstrumentedParts .notCovered(transformed)
180
204
181
205
private def tryInstrument (tree : Ident )(using Context ): InstrumentedParts =
@@ -187,9 +211,14 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
187
211
else
188
212
InstrumentedParts .notCovered(tree)
189
213
214
+ private def tryInstrument (tree : Literal )(using Context ): InstrumentedParts =
215
+ val coverageCall = createInvokeCall(tree, tree.sourcePos)
216
+ InstrumentedParts .singleExpr(coverageCall, tree)
217
+
190
218
private def tryInstrument (tree : Select )(using Context ): InstrumentedParts =
191
219
val sym = tree.symbol
192
- val transformed = cpy.Select (tree)(transform(tree.qualifier), tree.name)
220
+ val qual = transform(tree.qualifier).ensureConforms(tree.qualifier.tpe)
221
+ val transformed = cpy.Select (tree)(qual, tree.name)
193
222
if canInstrumentParameterless(sym) then
194
223
// call to a parameterless method
195
224
val coverageCall = createInvokeCall(tree, tree.sourcePos)
@@ -202,6 +231,7 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
202
231
tree match
203
232
case t : Apply => tryInstrument(t)
204
233
case t : Ident => tryInstrument(t)
234
+ case t : Literal => tryInstrument(t)
205
235
case t : Select => tryInstrument(t)
206
236
case _ => InstrumentedParts .notCovered(transform(tree))
207
237
@@ -223,10 +253,14 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
223
253
inContext(transformCtx(tree)) { // necessary to position inlined code properly
224
254
tree match
225
255
// simple cases
226
- case tree : (Import | Export | Literal | This | Super | New ) => tree
256
+ case tree : (Import | Export | This | Super | New ) => tree
227
257
case tree if tree.isEmpty || tree.isType => tree // empty Thicket, Ident (referring to a type), TypeTree, ...
228
258
case tree if ! tree.span.exists || tree.span.isZeroExtent => tree // no meaningful position
229
259
260
+ case tree : Literal =>
261
+ val rest = tryInstrument(tree).toTree
262
+ rest
263
+
230
264
// identifier
231
265
case tree : Ident =>
232
266
tryInstrument(tree).toTree
@@ -280,6 +314,9 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
280
314
case tree : CaseDef =>
281
315
transformCaseDef(tree)
282
316
317
+ case tree : ValDef if tree.symbol.is(Inline ) =>
318
+ tree // transforming inline vals will result in `inline value must be pure` errors
319
+
283
320
case tree : ValDef =>
284
321
// only transform the rhs
285
322
val rhs = transform(tree.rhs)
@@ -323,13 +360,13 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
323
360
)
324
361
325
362
case tree : Inlined =>
326
- // Ideally, tree.call would provide precise information about the inlined call ,
327
- // and we would use this information for the coverage report.
328
- // But PostTyper simplifies tree.call, so we can't report the actual method that was inlined .
329
- // In any case, the subtrees need to be repositioned right now, otherwise the
330
- // coverage statement will point to a potentially unreachable source file.
331
- val dropped = Inlines .dropInlined(tree) // drop and reposition
332
- transform(dropped) // transform the content of the Inlined
363
+ // Inlined code contents might come from another file (or project) ,
364
+ // which means that we cannot clearly designate which part of the inlined code
365
+ // was run using the API we are given .
366
+ // At best, we can show that the Inlined tree itself was reached.
367
+ // Additionally, Scala 2's coverage ignores macro calls entirely,
368
+ // so let's do that here too, also for regular inlined calls.
369
+ tree
333
370
334
371
// For everything else just recurse and transform
335
372
case _ =>
@@ -559,15 +596,14 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
559
596
private def isCompilerIntrinsicMethod (sym : Symbol )(using Context ): Boolean =
560
597
val owner = sym.maybeOwner
561
598
owner.exists && (
562
- owner.eq(defn.AnyClass ) ||
563
- owner.isPrimitiveValueClass ||
599
+ (owner.eq(defn.AnyClass ) && (sym == defn.Any_asInstanceOf || sym == defn.Any_isInstanceOf )) ||
564
600
owner.maybeOwner == defn.CompiletimePackageClass
565
601
)
566
602
567
603
object InstrumentCoverage :
568
604
val name : String = " instrumentCoverage"
569
605
val description : String = " instrument code for coverage checking"
570
- val ExcludeMethodFlags : FlagSet = Synthetic | Artifact | Erased
606
+ val ExcludeMethodFlags : FlagSet = Artifact | Erased
571
607
572
608
/**
573
609
* An instrumented Tree, in 3 parts.
0 commit comments