Skip to content

Commit faa289d

Browse files
authored
KotlinSafeCallOperatorFilter should not be affected by presence of pseudo instructions (#1956)
1 parent 7994089 commit faa289d

File tree

4 files changed

+48
-4
lines changed

4 files changed

+48
-4
lines changed

org.jacoco.core.test.validation.kotlin/src/org/jacoco/core/test/validation/kotlin/targets/KotlinSafeCallOperatorTarget.kt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,28 @@ object KotlinSafeCallOperatorTarget {
5757
}
5858

5959
private fun safeCallChainMultiline() {
60+
fun nullOnly(a: A?): String? =
61+
a // assertNotCovered()
62+
?.b // assertPartlyCovered(1, 1)
63+
?.c // assertPartlyCovered(1, 1)
64+
65+
fun nonNullOnly(a: A?): String? =
66+
a // assertFullyCovered()
67+
?.b // assertPartlyCovered(1, 1)
68+
?.c // assertFullyCovered(1, 1)
69+
70+
fun fullCoverage(a: A?): String? =
71+
a // assertFullyCovered()
72+
?.b // assertFullyCovered(0, 2)
73+
?.c // assertFullyCovered(0, 2)
74+
75+
nullOnly(null)
76+
nonNullOnly(A(B("")))
77+
fullCoverage(null)
78+
fullCoverage(A(B("")))
79+
}
80+
81+
private fun safeCallChainMultiline2() {
6082
fun nullOnly(a: A?): String? =
6183
a?.also { // assertPartlyCovered(1, 1)
6284
nop(it) // assertNotCovered()
@@ -125,6 +147,7 @@ object KotlinSafeCallOperatorTarget {
125147
safeCall()
126148
safeCallChain()
127149
safeCallChainMultiline()
150+
safeCallChainMultiline2()
128151
safeCallChainException()
129152
safeCallFollowedByElvis()
130153
safeCallChainFollowedByElvis()

org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilterTest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public void should_filter_optimized_safe_call_chain() {
8989
* fun example(a: A?): String? =
9090
* a
9191
* ?.b
92-
* ?.c
92+
* ?.c // line 6
9393
* </pre>
9494
*/
9595
@Test
@@ -108,6 +108,9 @@ public void should_filter_unoptimized_safe_call_chain() {
108108
m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "A", "getB", "()LB;", false);
109109

110110
m.visitVarInsn(Opcodes.ASTORE, 1);
111+
final Label lineNumberLabel = new Label();
112+
m.visitLabel(lineNumberLabel);
113+
m.visitLineNumber(6, lineNumberLabel);
111114
m.visitVarInsn(Opcodes.ALOAD, 1);
112115
m.visitJumpInsn(Opcodes.IFNULL, label1);
113116
final AbstractInsnNode ifNullInstruction2 = m.instructions.getLast();

org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinSafeCallOperatorFilter.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,12 @@ private static Collection<ArrayList<JumpInsnNode>> findChains(
108108
continue;
109109
}
110110
} else if (target.getOpcode() == Opcodes.ACONST_NULL) {
111-
if (i.getPrevious().getOpcode() != Opcodes.ALOAD) {
111+
final AbstractInsnNode p1 = preceding(i);
112+
if (p1.getOpcode() != Opcodes.ALOAD) {
112113
continue;
113114
}
114115
if (chain != null) {
115-
final AbstractInsnNode p1 = i.getPrevious();
116-
final AbstractInsnNode p2 = p1.getPrevious();
116+
final AbstractInsnNode p2 = preceding(p1);
117117
if (p2 == null || p2.getOpcode() != Opcodes.ASTORE
118118
|| ((VarInsnNode) p1).var != ((VarInsnNode) p2).var) {
119119
continue;
@@ -131,4 +131,19 @@ private static Collection<ArrayList<JumpInsnNode>> findChains(
131131
return chains.values();
132132
}
133133

134+
/**
135+
* @return non pseudo-instruction preceding given
136+
*/
137+
private static AbstractInsnNode preceding(AbstractInsnNode i) {
138+
if (i == null) {
139+
return null;
140+
}
141+
do {
142+
i = i.getPrevious();
143+
} while (i != null && (i.getType() == AbstractInsnNode.LABEL
144+
|| i.getType() == AbstractInsnNode.LINE
145+
|| i.getType() == AbstractInsnNode.FRAME));
146+
return i;
147+
}
148+
134149
}

org.jacoco.doc/docroot/doc/changes.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ <h3>New Features</h3>
3232
<li>Part of bytecode generated by the Kotlin compiler for elvis operator that
3333
follows safe call operator is filtered out during generation of report
3434
(GitHub <a href="https://github.com/jacoco/jacoco/issues/1814">#1814</a>).</li>
35+
<li>Part of bytecode generated by the Kotlin compiler for more cases of chained
36+
safe call operators is filtered out during generation of report
37+
(GitHub <a href="https://github.com/jacoco/jacoco/issues/1956">#1956</a>).</li>
3538
<li>Part of bytecode generated by the Kotlin compiler for invocations of
3639
<code>suspendCoroutineUninterceptedOrReturn</code> intrinsic is filtered out
3740
during generation of report

0 commit comments

Comments
 (0)