Skip to content

Commit

Permalink
Filter unreachable branches in Kotlin open functions with default arg…
Browse files Browse the repository at this point in the history
…uments (#887)
  • Loading branch information
Godin committed Jun 3, 2019
1 parent be17bad commit b7efb62
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 0 deletions.
Expand Up @@ -23,4 +23,9 @@ public KotlinInlineTest() {
super(KotlinInlineTargetKt.class);
}

@Override
public void all_missed_instructions_should_have_line_number() {
// missed instructions without line number in inline function
}

}
Expand Up @@ -22,6 +22,11 @@ object KotlinDefaultArgumentsTarget {
private fun branch(a: Boolean, b: String = if (a) "a" else "b") { // assertFullyCovered(0, 2)
}

open class Open {
open fun f(a: String = "a") { // assertFullyCovered()
}
}

@JvmStatic
fun main(args: Array<String>) {
f(a = "a")
Expand All @@ -31,6 +36,8 @@ object KotlinDefaultArgumentsTarget {

branch(false)
branch(true)

Open().f()
}

}
Expand Up @@ -95,4 +95,59 @@ public void should_not_filter_when_not_synthetic() {
assertIgnored();
}

/**
* <pre>
* open class Open {
* open fun foo(a: Int = 42) {
* }
* }
* </pre>
*/
@Test
public void should_filter_open_functions() {
final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION,
Opcodes.ACC_SYNTHETIC, "foo$default",
"(LOpen;IILjava/lang/Object;)V", null, null);
context.classAnnotations
.add(KotlinGeneratedFilter.KOTLIN_METADATA_DESC);
{
m.visitVarInsn(Opcodes.ALOAD, 3);
final Label label = new Label();
m.visitJumpInsn(Opcodes.IFNULL, label);
m.visitTypeInsn(Opcodes.NEW,
"java/lang/UnsupportedOperationException");
m.visitInsn(Opcodes.DUP);
m.visitLdcInsn(
"Super calls with default arguments not supported in this target, function: foo");
m.visitMethodInsn(Opcodes.INVOKESPECIAL,
"java/lang/UnsupportedOperationException", "<init>",
"(Ljava/lang/String;)V", false);
m.visitInsn(Opcodes.ATHROW);
m.visitLabel(label);
}
{
m.visitVarInsn(Opcodes.ILOAD, 2);
m.visitInsn(Opcodes.ICONST_1);
m.visitInsn(Opcodes.IAND);
final Label label = new Label();
m.visitJumpInsn(Opcodes.IFEQ, label);
// default argument
m.visitLdcInsn(Integer.valueOf(42));
m.visitVarInsn(Opcodes.ISTORE, 1);
m.visitLabel(label);

m.visitVarInsn(Opcodes.ALOAD, 0);
m.visitVarInsn(Opcodes.ILOAD, 1);
m.visitMethodInsn(Opcodes.INVOKESPECIAL, "Open", "foo", "(I)V",
false);
m.visitInsn(Opcodes.RETURN);
}

filter.filter(m, context, output);

assertIgnored(
new Range(m.instructions.getFirst(), m.instructions.get(6)),
new Range(m.instructions.get(11), m.instructions.get(11)));
}

}
Expand Up @@ -123,6 +123,28 @@ public void last_line_in_coverage_data_should_be_less_or_equal_to_number_of_line
source.getCoverage().getLastLine() <= source.getLines().size());
}

@Test
public void all_missed_instructions_should_have_line_number() {
CounterImpl c = CounterImpl.COUNTER_0_0;
for (Line line : source.getLines()) {
c = c.increment(line.getCoverage().getInstructionCounter());
}
assertEquals(
"sum of missed instructions of all lines should be equal to missed instructions of file",
source.getCoverage().getInstructionCounter().getMissedCount(), c.getMissedCount());
}

@Test
public void all_branches_should_have_line_number() {
CounterImpl c = CounterImpl.COUNTER_0_0;
for (Line line : source.getLines()) {
c = c.increment(line.getCoverage().getBranchCounter());
}
assertEquals(
"sum of branch counters of all lines should be equal to branch counter of file",
source.getCoverage().getBranchCounter(), c);
}

/*
* Predefined assertion methods:
*/
Expand Down
Expand Up @@ -18,6 +18,7 @@
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;

Expand Down Expand Up @@ -67,6 +68,27 @@ public void match(final MethodNode methodNode,
final IFilterOutput output) {
cursor = methodNode.instructions.getFirst();

nextIs(Opcodes.IFNULL);
nextIsType(Opcodes.NEW, "java/lang/UnsupportedOperationException");
nextIs(Opcodes.DUP);
nextIs(Opcodes.LDC);
if (cursor == null
|| !(((LdcInsnNode) cursor).cst instanceof String)
|| !(((String) ((LdcInsnNode) cursor).cst).startsWith(
"Super calls with default arguments not supported in this target"))) {
cursor = null;
}
nextIsInvoke(Opcodes.INVOKESPECIAL,
"java/lang/UnsupportedOperationException", "<init>",
"(Ljava/lang/String;)V");
nextIs(Opcodes.ATHROW);
if (cursor != null) {
output.ignore(methodNode.instructions.getFirst(), cursor);
next();
} else {
cursor = methodNode.instructions.getFirst();
}

final Set<AbstractInsnNode> ignore = new HashSet<AbstractInsnNode>();
final int maskVar = Type.getMethodType(methodNode.desc)
.getArgumentTypes().length - 2;
Expand Down
7 changes: 7 additions & 0 deletions org.jacoco.doc/docroot/doc/changes.html
Expand Up @@ -20,6 +20,13 @@ <h1>Change History</h1>

<h2>Snapshot Build @qualified.bundle.version@ (@build.date@)</h2>

<h3>New Features</h3>
<ul>
<li>Branches added by the Kotlin compiler for <code>open</code> functions with
default arguments are filtered out during generation of report
(GitHub <a href="https://github.com/jacoco/jacoco/issues/887">#887</a>).</li>
</ul>

<h2>Release 0.8.4 (2019/05/08)</h2>

<h3>New Features</h3>
Expand Down

0 comments on commit b7efb62

Please sign in to comment.