Skip to content

Commit 0865aab

Browse files
author
Vijay Ramesh
committed
wip
1 parent 4262be0 commit 0865aab

File tree

3 files changed

+75
-49
lines changed

3 files changed

+75
-49
lines changed

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

+45-19
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,20 @@ package dotty.tools.dotc
22
package transform
33

44
import java.io.File
5-
65
import ast.tpd.*
6+
77
import collection.mutable
88
import core.Flags.*
99
import core.Contexts.{Context, ctx, inContext}
1010
import core.DenotTransformers.IdentityDenotTransformer
11-
import core.Symbols.{defn, Symbol}
11+
import core.Symbols.{Symbol, defn}
1212
import core.Constants.Constant
1313
import core.NameOps.isContextFunction
1414
import core.StdNames.nme
1515
import core.Types.*
1616
import coverage.*
1717
import typer.LiftCoverage
18-
import util.{SourcePosition, SourceFile}
18+
import util.{SourceFile, SourcePosition}
1919
import util.Spans.Span
2020
import localopt.StringInterpolatorOpt
2121
import inlines.Inlines
@@ -120,6 +120,7 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
120120
*/
121121
private def createInvokeCall(tree: Tree, pos: SourcePosition, branch: Boolean = false)(using Context): Apply =
122122
val statementId = recordStatement(tree, pos, branch)
123+
println(s"createInvokeCall: $statementId")
123124
val span = pos.span.toSynthetic
124125
invokeCall(statementId, span)
125126

@@ -132,6 +133,8 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
132133
* @return instrumentation result, with the preparation statement, coverage call and tree separated
133134
*/
134135
private def tryInstrument(tree: Apply)(using Context): InstrumentedParts =
136+
println(s"tryInstrument Tree: ${tree}")
137+
135138
if canInstrumentApply(tree) then
136139
// Create a call to Invoker.invoked(coverageDirectory, newStatementId)
137140
val coverageCall = createInvokeCall(tree, tree.sourcePos)
@@ -156,20 +159,33 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
156159
val transformed = cpy.Apply(tree)(transform(tree.fun), transform(tree.args))
157160
InstrumentedParts.notCovered(transformed)
158161

159-
private def tryInstrument(tree: Ident)(using Context): InstrumentedParts =
162+
private def tryInstrument(tree: Ident)(using Context): InstrumentedParts = {
163+
println(s"tryInstrument Ident: ${tree}")
164+
165+
160166
val sym = tree.symbol
161-
if canInstrumentParameterless(sym) then
167+
168+
if (canInstrumentParameterless(sym)) {
162169
// call to a local parameterless method f
163170
val coverageCall = createInvokeCall(tree, tree.sourcePos)
164171
InstrumentedParts.singleExpr(coverageCall, tree)
165-
else
166-
InstrumentedParts.notCovered(tree)
172+
} else {
173+
println(s"tryInstrument Ident: ${tree} not instrumented")
174+
InstrumentedParts.notCovered(tree)
175+
}
176+
}
167177

168-
private def tryInstrument(tree: Select)(using Context): InstrumentedParts =
178+
private def tryInstrument(tree: Select)(using Context): InstrumentedParts = {
179+
val sym = tree.symbol
169180
val transformed = cpy.Select(tree)(transform(tree.qualifier), tree.name)
170-
val coverageCall = createInvokeCall(tree, tree.sourcePos)
171-
InstrumentedParts.singleExpr(coverageCall, transformed)
172-
181+
println(s"tryInstrument Select: ${tree}")
182+
if canInstrumentParameterless(sym) then
183+
// call to a parameterless method
184+
val coverageCall = createInvokeCall(tree, tree.sourcePos)
185+
InstrumentedParts.singleExpr(coverageCall, transformed)
186+
else
187+
InstrumentedParts.notCovered(transformed)
188+
}
173189
/** Generic tryInstrument */
174190
private def tryInstrument(tree: Tree)(using Context): InstrumentedParts =
175191
tree match
@@ -229,6 +245,7 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
229245
case TypeApply(fun, args) =>
230246
// Here is where `InstrumentedParts` becomes useful!
231247
// We extract its components and act carefully.
248+
println(s"TypeApply: ${fun}")
232249
val InstrumentedParts(pre, coverageCall, expr) = tryInstrument(fun)
233250

234251
if coverageCall.isEmpty then
@@ -439,6 +456,7 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
439456
* should not be changed to {val $x = f(); T($x)}(1) but to {val $x = f(); val $y = 1; T($x)($y)}
440457
*/
441458
private def needsLift(tree: Apply)(using Context): Boolean =
459+
println(s"start needsLift: $tree")
442460
def isShortCircuitedOp(sym: Symbol) =
443461
sym == defn.Boolean_&& || sym == defn.Boolean_||
444462

@@ -460,8 +478,10 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
460478
case a: Apply => needsLift(a)
461479
case _ => false
462480

463-
nestedApplyNeedsLift ||
481+
val liftMe = nestedApplyNeedsLift ||
464482
!isUnliftableFun(fun) && !tree.args.isEmpty && !tree.args.forall(LiftCoverage.noLift)
483+
println(s"end needsLift: $tree, liftMe: $liftMe, nestedApplyNeedsLift: $nestedApplyNeedsLift, isUnliftableFun: ${isUnliftableFun(fun)}, tree.args.isEmpty: ${tree.args}, tree.args.forall(LiftCoverage.noLift): ${tree.args.forall(LiftCoverage.noLift)}")
484+
liftMe
465485

466486
/** Check if an Apply can be instrumented. Prevents this phase from generating incorrect code. */
467487
private def canInstrumentApply(tree: Apply)(using Context): Boolean =
@@ -470,10 +490,10 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
470490
case _ => false
471491

472492
val sym = tree.symbol
473-
!sym.isOneOf(ExcludeMethodFlags)
493+
val canI = !sym.isOneOf(ExcludeMethodFlags)
474494
&& !isCompilerIntrinsicMethod(sym)
475495
&& !(sym.isClassConstructor && isSecondaryCtorDelegateCall)
476-
&& (tree.typeOpt match
496+
&& (tree.typeOpt match {
477497
case AppliedType(tycon: NamedType, _) =>
478498
/* If the last expression in a block is a context function, we'll try to
479499
summon its arguments at the current point, even if the expected type
@@ -496,19 +516,25 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
496516
*/
497517
false
498518
case _ =>
499-
true
500-
)
519+
false
520+
})
521+
println(s"canInstrumentApply ${tree.symbol}: $canI is ${sym.name} is method ${sym.is(Method)} is compiler intrinsic ${isCompilerIntrinsicMethod(sym)} typeOpt ${tree.typeOpt}")
522+
canI
523+
501524

502525
/** Is this the symbol of a parameterless method that we can instrument?
503526
* Note: it is crucial that `asInstanceOf` and `isInstanceOf`, among others,
504527
* do NOT get instrumented, because that would generate invalid code and crash
505528
* in post-erasure checking.
506529
*/
507-
private def canInstrumentParameterless(sym: Symbol)(using Context): Boolean =
508-
sym.is(Method, butNot = ExcludeMethodFlags)
530+
private def canInstrumentParameterless(sym: Symbol)(using Context): Boolean = {
531+
val isP = (sym.is(Method, butNot = ExcludeMethodFlags)
509532
&& sym.info.isParameterless
510533
&& !isCompilerIntrinsicMethod(sym)
511-
&& !sym.info.typeSymbol.name.isContextFunction // exclude context functions like in canInstrumentApply
534+
&& !sym.info.typeSymbol.name.isContextFunction ) // exclude context functions like in canInstrumentApply)
535+
println(s"canInstrumentParameterless $sym: $isP is ${sym.name} kind string ${sym.kindString} is method ${sym.is(Method)} is parameterless ${sym.info.isParameterless} is compiler intrinsic ${isCompilerIntrinsicMethod(sym)} typeSymbol ${sym.info.typeSymbol} is context function ${sym.info.typeSymbol.name.isContextFunction}")
536+
isP
537+
}
512538

513539
/** Does sym refer to a "compiler intrinsic" method, which only exist during compilation,
514540
* like Any.isInstanceOf?

compiler/test/dotty/tools/dotc/coverage/CoverageTests.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ import dotty.tools.dotc.util.DiffUtil
2121
class CoverageTests:
2222
import CoverageTests.{*, given}
2323

24-
private val scalaFile = FileSystems.getDefault.getPathMatcher("glob:**.scala")
24+
private val scalaFile = FileSystems.getDefault.getPathMatcher("glob:**for_comprehension**.scala")
2525
private val rootSrc = Paths.get(userDir, "tests", "coverage")
26-
26+
2727
@Test
2828
def checkCoverageStatements(): Unit =
2929
checkCoverageIn(rootSrc.resolve("pos"), false)
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
11
@main
22
def Test: Unit = {
3-
4-
def unreachableFunction(): Seq[Int] = {
5-
for {
6-
a <- List(1)
7-
b <- List(2)
8-
} yield {
9-
println(a)
10-
println(b)
11-
a + b
12-
}
13-
}
14-
15-
def unreachableFunctionUnlessTrue(flag: Boolean): Option[Int] = {
16-
if (flag) {
17-
val foo: Seq[Int] = for {
18-
a <- List(1)
19-
b <- List(2)
20-
} yield {
21-
println(a)
22-
println(b)
23-
a + b
24-
}
25-
foo.headOption
26-
} else {
27-
None
28-
}
29-
}
3+
//
4+
// def unreachableFunction(): Seq[Int] = {
5+
// for {
6+
// a <- List(1)
7+
// b <- List(2)
8+
// } yield {
9+
// println(a)
10+
// println(b)
11+
// a + b
12+
// }
13+
// }
14+
//
15+
// def unreachableFunctionUnlessTrue(flag: Boolean): Option[Int] = {
16+
// if (flag) {
17+
// val foo: Seq[Int] = for {
18+
// a <- List(1)
19+
// b <- List(2)
20+
// } yield {
21+
// println(a)
22+
// println(b)
23+
// a + b
24+
// }
25+
// foo.headOption
26+
// } else {
27+
// None
28+
// }
29+
// }
3030

3131
for {
3232
a <- List(1)
@@ -37,5 +37,5 @@ def Test: Unit = {
3737
(a, b)
3838
}
3939

40-
unreachableFunctionUnlessTrue(false)
40+
// unreachableFunctionUnlessTrue(false)
4141
}

0 commit comments

Comments
 (0)