Skip to content

Commit 743f3cc

Browse files
Coverage: reposition inlined trees to avoid unreachable sources, fix #15791
1 parent 7fc537c commit 743f3cc

File tree

6 files changed

+327
-13
lines changed

6 files changed

+327
-13
lines changed

compiler/src/dotty/tools/dotc/coverage/Coverage.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ class Coverage:
1313

1414
/** A statement that can be invoked, and thus counted as "covered" by code coverage tools. */
1515
case class Statement(
16-
source: String,
1716
location: Location,
1817
id: Int,
1918
start: Int,

compiler/src/dotty/tools/dotc/coverage/Location.scala

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,35 @@ import ast.tpd._
55
import dotty.tools.dotc.core.Contexts.Context
66
import dotty.tools.dotc.core.Flags.*
77
import java.nio.file.Path
8+
import dotty.tools.dotc.util.SourceFile
89

910
/** Information about the location of a coverable piece of code.
1011
*
1112
* @param packageName name of the enclosing package
1213
* @param className name of the closest enclosing class
1314
* @param fullClassName fully qualified name of the closest enclosing class
1415
* @param classType "type" of the closest enclosing class: Class, Trait or Object
15-
* @param method name of the closest enclosing method
16+
* @param method name of the closest enclosing method
1617
* @param sourcePath absolute path of the source file
1718
*/
1819
final case class Location(
1920
packageName: String,
2021
className: String,
2122
fullClassName: String,
2223
classType: String,
23-
method: String,
24+
methodName: String,
2425
sourcePath: Path
2526
)
2627

2728
object Location:
2829
/** Extracts the location info of a Tree. */
29-
def apply(tree: Tree)(using ctx: Context): Location =
30+
def apply(tree: Tree, source: SourceFile)(using ctx: Context): Location =
3031

31-
val enclosingClass = ctx.owner.denot.enclosingClass
32-
val packageName = ctx.owner.denot.enclosingPackageClass.name.toSimpleName.toString
32+
val ownerDenot = ctx.owner.denot
33+
val enclosingClass = ownerDenot.enclosingClass
34+
val packageName = ownerDenot.enclosingPackageClass.name.toSimpleName.toString
3335
val className = enclosingClass.name.toSimpleName.toString
36+
val methodName = ownerDenot.enclosingMethod.name.toSimpleName.toString
3437

3538
val classType: String =
3639
if enclosingClass.is(Trait) then "Trait"
@@ -42,6 +45,6 @@ object Location:
4245
className,
4346
s"$packageName.$className",
4447
classType,
45-
ctx.owner.denot.enclosingMethod.name.toSimpleName.toString(),
46-
ctx.source.file.absolute.jpath
48+
methodName,
49+
source.file.absolute.jpath
4750
)

compiler/src/dotty/tools/dotc/coverage/Serializer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ object Serializer:
6767
|${stmt.location.className}
6868
|${stmt.location.classType}
6969
|${stmt.location.fullClassName}
70-
|${stmt.location.method}
70+
|${stmt.location.methodName}
7171
|${stmt.start}
7272
|${stmt.end}
7373
|${stmt.line}

compiler/src/dotty/tools/dotc/transform/InstrumentCoverage.scala

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ import core.NameOps.isContextFunction
1414
import core.Types.*
1515
import coverage.*
1616
import typer.LiftCoverage
17-
import util.SourcePosition
17+
import util.{SourcePosition, SourceFile}
1818
import util.Spans.Span
1919
import localopt.StringInterpolatorOpt
20+
import inlines.Inlines
2021

2122
/** Implements code coverage by inserting calls to scala.runtime.coverage.Invoker
2223
* ("instruments" the source code).
@@ -88,14 +89,15 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
8889
private def recordStatement(tree: Tree, pos: SourcePosition, branch: Boolean)(using ctx: Context): Int =
8990
val id = statementId
9091
statementId += 1
92+
93+
val sourceFile = pos.source
9194
val statement = Statement(
92-
source = ctx.source.file.name,
93-
location = Location(tree),
95+
location = Location(tree, sourceFile),
9496
id = id,
9597
start = pos.start,
9698
end = pos.end,
9799
line = pos.line,
98-
desc = tree.source.content.slice(pos.start, pos.end).mkString,
100+
desc = sourceFile.content.slice(pos.start, pos.end).mkString,
99101
symbolName = tree.symbol.name.toSimpleName.toString,
100102
treeName = tree.getClass.getSimpleName.nn,
101103
branch
@@ -291,6 +293,15 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
291293
transformStats(tree.body, tree.symbol)
292294
)
293295

296+
case tree: Inlined =>
297+
// Ideally, tree.call would provide precise information about the inlined call,
298+
// and we would use this information for the coverage report.
299+
// But PostTyper simplifies tree.call, so we can't report the actual method that was inlined.
300+
// In any case, the subtrees need to be repositioned right now, otherwise the
301+
// coverage statement will point to a potentially unreachable source file.
302+
val dropped = Inlines.dropInlined(tree) // drop and reposition
303+
transform(dropped) // transform the content of the Inlined
304+
294305
// For everything else just recurse and transform
295306
case _ =>
296307
super.transform(tree)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package covtest
2+
3+
// assert is a `transparent inline` in Predef,
4+
// but its source path should not appear in the coverage report.
5+
def testInlined(): Unit =
6+
val l = 1
7+
assert(l == 1)
8+
assert(l == List(l).length)
9+
assert(List(l).length == 1)

0 commit comments

Comments
 (0)