@@ -145,6 +145,31 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
145145 val span = pos.span.toSynthetic
146146 invokeCall(statementId, span)
147147
148+ private def transformApplyArgs (trees : List [Tree ])(using Context ): List [Tree ] =
149+ if allConstArgs(trees) then 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 t : Typed =>
168+ cpy.Typed (t)(transformInnerApply(t.expr), t.tpt)
169+ case other => transform(other)
170+
171+ private def allConstArgs (args : List [Tree ]) =
172+ args.forall(arg => arg.isInstanceOf [Literal ] || arg.isInstanceOf [Ident ])
148173 /**
149174 * Tries to instrument an `Apply`.
150175 * These "tryInstrument" methods are useful to tweak the generation of coverage instrumentation,
@@ -158,10 +183,12 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
158183 // Create a call to Invoker.invoked(coverageDirectory, newStatementId)
159184 val coverageCall = createInvokeCall(tree, tree.sourcePos)
160185
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))
186+ // Transform args and fun, i.e. instrument them if needed (and if possible)
187+ val app =
188+ if tree.fun.symbol eq defn.throwMethod then tree
189+ else cpy.Apply (tree)(transformInnerApply(tree.fun), transformApplyArgs(tree.args))
164190
191+ if needsLift(tree) then
165192 // Lifts the arguments. Note that if only one argument needs to be lifted, we lift them all.
166193 // Also, tree.fun can be lifted too.
167194 // See LiftCoverage for the internal working of this lifting.
@@ -171,11 +198,10 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
171198 InstrumentedParts (liftedDefs.toList, coverageCall, liftedApp)
172199 else
173200 // Instrument without lifting
174- val transformed = cpy.Apply (tree)(transform(tree.fun), transform(tree.args))
175- InstrumentedParts .singleExpr(coverageCall, transformed)
201+ InstrumentedParts .singleExpr(coverageCall, app)
176202 else
177203 // Transform recursively but don't instrument the tree itself
178- val transformed = cpy.Apply (tree)(transform (tree.fun), transform(tree.args))
204+ val transformed = cpy.Apply (tree)(transformInnerApply (tree.fun), transform(tree.args))
179205 InstrumentedParts .notCovered(transformed)
180206
181207 private def tryInstrument (tree : Ident )(using Context ): InstrumentedParts =
@@ -187,9 +213,14 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
187213 else
188214 InstrumentedParts .notCovered(tree)
189215
216+ private def tryInstrument (tree : Literal )(using Context ): InstrumentedParts =
217+ val coverageCall = createInvokeCall(tree, tree.sourcePos)
218+ InstrumentedParts .singleExpr(coverageCall, tree)
219+
190220 private def tryInstrument (tree : Select )(using Context ): InstrumentedParts =
191221 val sym = tree.symbol
192- val transformed = cpy.Select (tree)(transform(tree.qualifier), tree.name)
222+ val qual = transform(tree.qualifier).ensureConforms(tree.qualifier.tpe)
223+ val transformed = cpy.Select (tree)(qual, tree.name)
193224 if canInstrumentParameterless(sym) then
194225 // call to a parameterless method
195226 val coverageCall = createInvokeCall(tree, tree.sourcePos)
@@ -202,6 +233,7 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
202233 tree match
203234 case t : Apply => tryInstrument(t)
204235 case t : Ident => tryInstrument(t)
236+ case t : Literal => tryInstrument(t)
205237 case t : Select => tryInstrument(t)
206238 case _ => InstrumentedParts .notCovered(transform(tree))
207239
@@ -223,10 +255,14 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
223255 inContext(transformCtx(tree)) { // necessary to position inlined code properly
224256 tree match
225257 // simple cases
226- case tree : (Import | Export | Literal | This | Super | New ) => tree
258+ case tree : (Import | Export | This | Super | New ) => tree
227259 case tree if tree.isEmpty || tree.isType => tree // empty Thicket, Ident (referring to a type), TypeTree, ...
228260 case tree if ! tree.span.exists || tree.span.isZeroExtent => tree // no meaningful position
229261
262+ case tree : Literal =>
263+ val rest = tryInstrument(tree).toTree
264+ rest
265+
230266 // identifier
231267 case tree : Ident =>
232268 tryInstrument(tree).toTree
@@ -280,6 +316,9 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
280316 case tree : CaseDef =>
281317 transformCaseDef(tree)
282318
319+ case tree : ValDef if tree.symbol.is(Inline ) =>
320+ tree // transforming inline vals will result in `inline value must be pure` errors
321+
283322 case tree : ValDef =>
284323 // only transform the rhs
285324 val rhs = transform(tree.rhs)
@@ -323,13 +362,13 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
323362 )
324363
325364 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
365+ // Inlined code contents might come from another file (or project) ,
366+ // which means that we cannot clearly designate which part of the inlined code
367+ // was run using the API we are given .
368+ // At best, we can show that the Inlined tree itself was reached.
369+ // Additionally, Scala 2's coverage ignores macro calls entirely,
370+ // so let's do that here too, also for regular inlined calls.
371+ tree
333372
334373 // For everything else just recurse and transform
335374 case _ =>
@@ -559,15 +598,14 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
559598 private def isCompilerIntrinsicMethod (sym : Symbol )(using Context ): Boolean =
560599 val owner = sym.maybeOwner
561600 owner.exists && (
562- owner.eq(defn.AnyClass ) ||
563- owner.isPrimitiveValueClass ||
601+ (owner.eq(defn.AnyClass ) && (sym == defn.Any_asInstanceOf || sym == defn.Any_isInstanceOf )) ||
564602 owner.maybeOwner == defn.CompiletimePackageClass
565603 )
566604
567605object InstrumentCoverage :
568606 val name : String = " instrumentCoverage"
569607 val description : String = " instrument code for coverage checking"
570- val ExcludeMethodFlags : FlagSet = Synthetic | Artifact | Erased
608+ val ExcludeMethodFlags : FlagSet = Artifact | Erased
571609
572610 /**
573611 * An instrumented Tree, in 3 parts.
0 commit comments