Skip to content

Commit 1bf635b

Browse files
authored
Fix #17042: Preserve the shape of secondary ctors in instrumentCoverage. (#17111)
2 parents 4fd38e5 + 5fb69be commit 1bf635b

File tree

3 files changed

+154
-31
lines changed

3 files changed

+154
-31
lines changed

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

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import core.DenotTransformers.IdentityDenotTransformer
1111
import core.Symbols.{defn, Symbol}
1212
import core.Constants.Constant
1313
import core.NameOps.isContextFunction
14+
import core.StdNames.nme
1415
import core.Types.*
1516
import coverage.*
1617
import typer.LiftCoverage
@@ -325,7 +326,11 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
325326
// Only transform the params (for the default values) and the rhs, not the name and tpt.
326327
val transformedParamss = transformParamss(tree.paramss)
327328
val transformedRhs =
328-
if !sym.isOneOf(Accessor | Artifact | Synthetic) && !tree.rhs.isEmpty then
329+
if tree.rhs.isEmpty then
330+
tree.rhs
331+
else if sym.isClassConstructor then
332+
instrumentSecondaryCtor(tree)
333+
else if !sym.isOneOf(Accessor | Artifact | Synthetic) then
329334
// If the body can be instrumented, do it (i.e. insert a "coverage call" at the beginning)
330335
// This is useful because methods can be stored and called later, or called by reflection,
331336
// and if the rhs is too simple to be instrumented (like `def f = this`),
@@ -410,6 +415,24 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
410415
val coverageCall = createInvokeCall(parent, pos)
411416
InstrumentedParts.singleExprTree(coverageCall, body)
412417

418+
/** Instruments the body of a secondary constructor DefDef.
419+
*
420+
* We must preserve the delegate constructor call as the first statement of
421+
* the rhs Block, otherwise `HoistSuperArgs` will not be happy (see #17042).
422+
*/
423+
private def instrumentSecondaryCtor(ctorDef: DefDef)(using Context): Tree =
424+
// compute position like in instrumentBody
425+
val namePos = ctorDef.namePos
426+
val pos = namePos.withSpan(namePos.span.withStart(ctorDef.span.start))
427+
val coverageCall = createInvokeCall(ctorDef, pos)
428+
429+
ctorDef.rhs match
430+
case b @ Block(delegateCtorCall :: stats, expr: Literal) =>
431+
cpy.Block(b)(transform(delegateCtorCall) :: coverageCall :: stats.mapConserve(transform), expr)
432+
case rhs =>
433+
cpy.Block(rhs)(transform(rhs) :: coverageCall :: Nil, unitLiteral)
434+
end instrumentSecondaryCtor
435+
413436
/**
414437
* Checks if the apply needs a lift in the coverage phase.
415438
* In case of a nested application, we have to lift all arguments
@@ -447,9 +470,14 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
447470

448471
/** Check if an Apply can be instrumented. Prevents this phase from generating incorrect code. */
449472
private def canInstrumentApply(tree: Apply)(using Context): Boolean =
473+
def isSecondaryCtorDelegateCall: Boolean = tree.fun match
474+
case Select(This(_), nme.CONSTRUCTOR) => true
475+
case _ => false
476+
450477
val sym = tree.symbol
451478
!sym.isOneOf(ExcludeMethodFlags)
452479
&& !isCompilerIntrinsicMethod(sym)
480+
&& !(sym.isClassConstructor && isSecondaryCtorDelegateCall)
453481
&& (tree.typeOpt match
454482
case AppliedType(tycon: NamedType, _) =>
455483
/* If the last expression in a block is a context function, we'll try to

tests/coverage/pos/Constructor.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
package covtest
22

33
class C:
4+
def this(arg: String) = {
5+
this()
6+
g()
7+
}
8+
9+
def this(x: Int) =
10+
this(x.toString() + "foo")
11+
412
def f(x: Int) = x
513
def x = 1
614
f(x)
715

16+
def g(): Int = 2
17+
818
object O:
919
def g(y: Int) = y
1020
def y = 1

tests/coverage/pos/Constructor.scoverage.check

Lines changed: 115 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,129 +24,214 @@ covtest
2424
C
2525
Class
2626
covtest.C
27-
f
27+
<init>
2828
28
29-
33
29+
36
3030
3
31+
<init>
32+
DefDef
33+
false
34+
0
35+
false
36+
def this
37+
38+
1
39+
Constructor.scala
40+
covtest
41+
C
42+
Class
43+
covtest.C
44+
<init>
45+
69
46+
72
47+
5
48+
g
49+
Apply
50+
false
51+
0
52+
false
53+
g()
54+
55+
2
56+
Constructor.scala
57+
covtest
58+
C
59+
Class
60+
covtest.C
61+
<init>
62+
80
63+
88
64+
8
65+
<init>
66+
DefDef
67+
false
68+
0
69+
false
70+
def this
71+
72+
3
73+
Constructor.scala
74+
covtest
75+
C
76+
Class
77+
covtest.C
78+
<init>
79+
108
80+
128
81+
9
82+
+
83+
Apply
84+
false
85+
0
86+
false
87+
x.toString() + "foo"
88+
89+
4
90+
Constructor.scala
91+
covtest
92+
C
93+
Class
94+
covtest.C
95+
f
96+
133
97+
138
98+
11
3199
f
32100
DefDef
33101
false
34102
0
35103
false
36104
def f
37105

38-
1
106+
5
39107
Constructor.scala
40108
covtest
41109
C
42110
Class
43111
covtest.C
44112
x
45-
48
46-
53
47-
4
113+
153
114+
158
115+
12
48116
x
49117
DefDef
50118
false
51119
0
52120
false
53121
def x
54122

55-
2
123+
6
56124
Constructor.scala
57125
covtest
58126
C
59127
Class
60128
covtest.C
61129
<init>
62-
60
63-
64
64-
5
130+
165
131+
169
132+
13
65133
f
66134
Apply
67135
false
68136
0
69137
false
70138
f(x)
71139

72-
3
140+
7
73141
Constructor.scala
74142
covtest
75143
C
76144
Class
77145
covtest.C
78146
<init>
79-
62
80-
63
81-
5
147+
167
148+
168
149+
13
82150
x
83151
Select
84152
false
85153
0
86154
false
87155
x
88156

89-
4
157+
8
158+
Constructor.scala
159+
covtest
160+
C
161+
Class
162+
covtest.C
163+
g
164+
173
165+
178
166+
15
167+
g
168+
DefDef
169+
false
170+
0
171+
false
172+
def g
173+
174+
9
90175
Constructor.scala
91176
covtest
92177
O$
93178
Object
94179
covtest.O$
95180
g
96-
78
97-
83
98-
8
181+
203
182+
208
183+
18
99184
g
100185
DefDef
101186
false
102187
0
103188
false
104189
def g
105190

106-
5
191+
10
107192
Constructor.scala
108193
covtest
109194
O$
110195
Object
111196
covtest.O$
112197
y
113-
98
114-
103
115-
9
198+
223
199+
228
200+
19
116201
y
117202
DefDef
118203
false
119204
0
120205
false
121206
def y
122207

123-
6
208+
11
124209
Constructor.scala
125210
covtest
126211
O$
127212
Object
128213
covtest.O$
129214
<init>
130-
110
131-
114
132-
10
215+
235
216+
239
217+
20
133218
g
134219
Apply
135220
false
136221
0
137222
false
138223
g(y)
139224

140-
7
225+
12
141226
Constructor.scala
142227
covtest
143228
O$
144229
Object
145230
covtest.O$
146231
<init>
147-
112
148-
113
149-
10
232+
237
233+
238
234+
20
150235
y
151236
Ident
152237
false

0 commit comments

Comments
 (0)