@@ -19,6 +19,8 @@ import util.{SourcePosition, SourceFile}
19
19
import util .Spans .Span
20
20
import localopt .StringInterpolatorOpt
21
21
import inlines .Inlines
22
+ import dotty .tools .dotc .ast .tpd
23
+ import dotty .tools .dotc .ast .untpd
22
24
23
25
/** Implements code coverage by inserting calls to scala.runtime.coverage.Invoker
24
26
* ("instruments" the source code).
@@ -300,7 +302,64 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
300
302
// But PostTyper simplifies tree.call, so we can't report the actual method that was inlined.
301
303
// In any case, the subtrees need to be repositioned right now, otherwise the
302
304
// coverage statement will point to a potentially unreachable source file.
303
- val dropped = Inlines .dropInlined(tree) // drop and reposition
305
+
306
+ /** Replace `Inlined` node by a block that contains its bindings and expansion */
307
+ def dropInlinedInstrumentaton (inlined : Inlined )(using Context ): Tree =
308
+ val tree1 =
309
+ if inlined.bindings.isEmpty then inlined.expansion
310
+ else cpy.Block (inlined)(inlined.bindings, inlined.expansion)
311
+ // Reposition in the outer most inlined call
312
+ if (enclosingInlineds.nonEmpty) tree1 else reposition(tree1, inlined.span)
313
+
314
+ def reposition (tree : Tree , callSpan : Span )(using Context ): Tree =
315
+ // Reference test tests/run/i4947b
316
+
317
+ val curSource = ctx.compilationUnit.source
318
+
319
+ // Tree copier that changes the source of all trees to `curSource`
320
+ val cpyWithNewSource = new TypedTreeCopier {
321
+ override protected def sourceFile (tree : tpd.Tree ): SourceFile = curSource
322
+ override protected val untpdCpy : untpd.UntypedTreeCopier = new untpd.UntypedTreeCopier {
323
+ override protected def sourceFile (tree : untpd.Tree ): SourceFile = curSource
324
+ }
325
+ }
326
+
327
+ /** Removes all Inlined trees, replacing them with blocks.
328
+ * Repositions all trees directly inside an inlined expansion of a non empty call to the position of the call.
329
+ * Any tree directly inside an empty call (inlined in the inlined code) retains their position.
330
+ *
331
+ * Until we implement JSR-45, we cannot represent in output positions in other source files.
332
+ * So, reposition inlined code from other files with the call position.
333
+ */
334
+ class Reposition extends TreeMap (cpyWithNewSource) {
335
+
336
+ override def transform (tree : Tree )(using Context ): Tree = {
337
+ def fixSpan [T <: untpd.Tree ](copied : T ): T =
338
+ copied.withSpan(if tree.source == curSource then tree.span else callSpan)
339
+ def finalize (copied : untpd.Tree ) =
340
+ fixSpan(copied).withAttachmentsFrom(tree).withTypeUnchecked(tree.tpe)
341
+
342
+ inContext(ctx.withSource(curSource)) {
343
+ tree match
344
+ case tree : Ident => finalize(untpd.Ident (tree.name)(curSource))
345
+ case tree : Literal => finalize(untpd.Literal (tree.const)(curSource))
346
+ case tree : This => finalize(untpd.This (tree.qual)(curSource))
347
+ case tree : JavaSeqLiteral => finalize(untpd.JavaSeqLiteral (transform(tree.elems), transform(tree.elemtpt))(curSource))
348
+ case tree : SeqLiteral => finalize(untpd.SeqLiteral (transform(tree.elems), transform(tree.elemtpt))(curSource))
349
+ case tree : Bind => finalize(untpd.Bind (tree.name, transform(tree.body))(curSource))
350
+ case tree : TypeTree => finalize(tpd.TypeTree (tree.tpe))
351
+ case tree : DefTree => super .transform(tree).setDefTree
352
+ case EmptyTree => tree
353
+ case _ => fixSpan(super .transform(tree))
354
+ }
355
+ }
356
+ }
357
+
358
+ (new Reposition ).transform(tree)
359
+ end reposition
360
+
361
+
362
+ val dropped = dropInlinedInstrumentaton(tree) // drop and reposition
304
363
transform(dropped) // transform the content of the Inlined
305
364
306
365
// For everything else just recurse and transform
0 commit comments