Skip to content

Commit b363ef3

Browse files
Keep fun and args together when instrumenting TypeApply, fixes #15705
1 parent 1ab427b commit b363ef3

File tree

5 files changed

+263
-3
lines changed

5 files changed

+263
-3
lines changed

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

+32-3
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,24 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
101101

102102
// (fun)[args]
103103
case TypeApply(fun, args) =>
104-
cpy.TypeApply(tree)(transform(fun), args)
104+
val tfun = transform(fun)
105+
tfun match
106+
case InstrumentCoverage.InstrumentedBlock(invokeCall, expr) =>
107+
// expr[T] shouldn't be transformed to
108+
// {invoked(...), expr}[T]
109+
//
110+
// but to
111+
// {invoked(...), expr[T]}
112+
//
113+
// This is especially important for trees like (expr[T])(args),
114+
// for which the wrong transformation crashes the compiler.
115+
// See tests/coverage/pos/PolymorphicExtensions.scala
116+
Block(
117+
invokeCall :: Nil,
118+
cpy.TypeApply(tree)(expr, args)
119+
)
120+
case _ =>
121+
cpy.TypeApply(tree)(tfun, args)
105122

106123
// a.b
107124
case Select(qual, name) =>
@@ -242,12 +259,12 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
242259
val statementId = recordStatement(parent, pos, false)
243260
insertInvokeCall(body, pos, statementId)
244261

245-
/** Returns the tree, prepended by a call to Invoker.invoker */
262+
/** Returns the tree, prepended by a call to Invoker.invoked */
246263
private def insertInvokeCall(tree: Tree, pos: SourcePosition, statementId: Int)(using Context): Tree =
247264
val callSpan = syntheticSpan(pos)
248265
Block(invokeCall(statementId, callSpan) :: Nil, tree).withSpan(callSpan.union(tree.span))
249266

250-
/** Generates Invoked.invoked(id, DIR) */
267+
/** Generates Invoker.invoked(id, DIR) */
251268
private def invokeCall(id: Int, span: Span)(using Context): Tree =
252269
val outputPath = ctx.settings.coverageOutputDir.value
253270
ref(defn.InvokedMethodRef).withSpan(span)
@@ -353,3 +370,15 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
353370
object InstrumentCoverage:
354371
val name: String = "instrumentCoverage"
355372
val description: String = "instrument code for coverage checking"
373+
374+
/** Extractor object for trees produced by `insertInvokeCall`. */
375+
object InstrumentedBlock:
376+
private def isInvokedCall(app: Apply)(using Context): Boolean =
377+
app.span.isSynthetic && app.symbol == defn.InvokedMethodRef.symbol
378+
379+
def unapply(t: Tree)(using Context): Option[(Apply, Tree)] =
380+
t match
381+
case Block((app: Apply) :: Nil, expr) if isInvokedCall(app) =>
382+
Some((app, expr))
383+
case _ =>
384+
None
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package covtest
2+
3+
object PolyExt:
4+
extension (s: String)
5+
def foo[A](x: A): A = x
6+
7+
extension [A](i: Int)
8+
def get(x: A): A = x
9+
10+
"str".foo(0) // ({foo("str")}[type])(0) i.e. Apply(TypeApply( Apply(foo, "str"), type ), List(0))
11+
123.get(0) // {(get[type])(123)}(0) i.e. Apply(Apply(TypeApply(...), List(123)), List(0))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Coverage data, format version: 3.0
2+
# Statement data:
3+
# - id
4+
# - source path
5+
# - package name
6+
# - class name
7+
# - class type (Class, Object or Trait)
8+
# - full class name
9+
# - method name
10+
# - start offset
11+
# - end offset
12+
# - line number
13+
# - symbol name
14+
# - tree name
15+
# - is branch
16+
# - invocations count
17+
# - is ignored
18+
# - description (can be multi-line)
19+
# ' ' sign
20+
# ------------------------------------------
21+
0
22+
PolymorphicExtensions.scala
23+
covtest
24+
PolyExt$
25+
Object
26+
covtest.PolyExt$
27+
foo
28+
61
29+
68
30+
4
31+
foo
32+
DefDef
33+
false
34+
0
35+
false
36+
def foo
37+
38+
1
39+
PolymorphicExtensions.scala
40+
covtest
41+
PolyExt$
42+
Object
43+
covtest.PolyExt$
44+
get
45+
114
46+
121
47+
7
48+
get
49+
DefDef
50+
false
51+
0
52+
false
53+
def get
54+
55+
2
56+
PolymorphicExtensions.scala
57+
covtest
58+
PolyExt$
59+
Object
60+
covtest.PolyExt$
61+
<init>
62+
138
63+
147
64+
9
65+
foo
66+
Apply
67+
false
68+
0
69+
false
70+
"str".foo
71+
72+
3
73+
PolymorphicExtensions.scala
74+
covtest
75+
PolyExt$
76+
Object
77+
covtest.PolyExt$
78+
<init>
79+
138
80+
150
81+
9
82+
<none>
83+
Apply
84+
false
85+
0
86+
false
87+
"str".foo(0)
88+
89+
4
90+
PolymorphicExtensions.scala
91+
covtest
92+
PolyExt$
93+
Object
94+
covtest.PolyExt$
95+
<init>
96+
238
97+
248
98+
10
99+
get
100+
Apply
101+
false
102+
0
103+
false
104+
123.get(0)
105+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package covtest
2+
3+
object PolyMeth:
4+
def f[A](x: A): A = x
5+
this.f(0) // (this.f[type])(0) i.e. Apply(TypeApply(Select(this,f), type), List(0))
6+
7+
C[String]().f("str", 0)
8+
9+
class C[T1]:
10+
def f[T2](p1: T1, p2: T2): Unit = ()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Coverage data, format version: 3.0
2+
# Statement data:
3+
# - id
4+
# - source path
5+
# - package name
6+
# - class name
7+
# - class type (Class, Object or Trait)
8+
# - full class name
9+
# - method name
10+
# - start offset
11+
# - end offset
12+
# - line number
13+
# - symbol name
14+
# - tree name
15+
# - is branch
16+
# - invocations count
17+
# - is ignored
18+
# - description (can be multi-line)
19+
# ' ' sign
20+
# ------------------------------------------
21+
0
22+
PolymorphicMethods.scala
23+
covtest
24+
PolyMeth$
25+
Object
26+
covtest.PolyMeth$
27+
f
28+
36
29+
41
30+
3
31+
f
32+
DefDef
33+
false
34+
0
35+
false
36+
def f
37+
38+
1
39+
PolymorphicMethods.scala
40+
covtest
41+
PolyMeth$
42+
Object
43+
covtest.PolyMeth$
44+
<init>
45+
60
46+
69
47+
4
48+
f
49+
Apply
50+
false
51+
0
52+
false
53+
this.f(0)
54+
55+
2
56+
PolymorphicMethods.scala
57+
covtest
58+
PolyMeth$
59+
Object
60+
covtest.PolyMeth$
61+
<init>
62+
147
63+
158
64+
6
65+
<init>
66+
Apply
67+
false
68+
0
69+
false
70+
C[String]()
71+
72+
3
73+
PolymorphicMethods.scala
74+
covtest
75+
PolyMeth$
76+
Object
77+
covtest.PolyMeth$
78+
<init>
79+
147
80+
170
81+
6
82+
f
83+
Apply
84+
false
85+
0
86+
false
87+
C[String]().f("str", 0)
88+
89+
4
90+
PolymorphicMethods.scala
91+
covtest
92+
C
93+
Class
94+
covtest.C
95+
f
96+
187
97+
192
98+
9
99+
f
100+
DefDef
101+
false
102+
0
103+
false
104+
def f
105+

0 commit comments

Comments
 (0)