diff --git a/core/src/main/kotlin/com/toasttab/expediter/Expediter.kt b/core/src/main/kotlin/com/toasttab/expediter/Expediter.kt index 4662246..aa08165 100644 --- a/core/src/main/kotlin/com/toasttab/expediter/Expediter.kt +++ b/core/src/main/kotlin/com/toasttab/expediter/Expediter.kt @@ -183,9 +183,9 @@ private class MemberWithDeclaringType( ) private fun ResolvedTypeHierarchy.CompleteTypeHierarchy.filterToAccessType(access: MemberAccess): Sequence { - return if (access is MemberAccess.MethodAccess && access.accessType == MethodAccessType.SPECIAL) { - // invokespecial dispatches statically, i.e. the method must be declared on the target type; - // it is used to invoke constructors, super methods, and private methods + return if (access is MemberAccess.MethodAccess && access.accessType == MethodAccessType.SPECIAL && access.ref.isConstructor()) { + // when invoking constructors, invokespecial dispatches statically + // when invoking super methods, invokespecial dispatches mostly dynamically sequenceOf(type) } else { // fields and methods, except for constructors can be declared on any type in the hierarchy diff --git a/tests/src/testFixtures/java/com/toasttab/expediter/test/caller/CallerNegative.java b/tests/src/testFixtures/java/com/toasttab/expediter/test/caller/CallerNegative.java index f727fc4..23db2f4 100644 --- a/tests/src/testFixtures/java/com/toasttab/expediter/test/caller/CallerNegative.java +++ b/tests/src/testFixtures/java/com/toasttab/expediter/test/caller/CallerNegative.java @@ -20,10 +20,11 @@ import java.lang.invoke.MethodType; import java.lang.invoke.VarHandle; import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.List; import java.util.SortedSet; -public class CallerNegative { +public class CallerNegative extends LinkedHashMap { private int x; void arrayCloneIsOk() { @@ -86,4 +87,9 @@ void varHandle() throws Throwable { vh.get(new int[0]); } + + void superSuperCallOk() { + // `invokespecial LinkedHashMap.remove(Object)` should be ok even though `remove` is declared on `HashMap` + super.remove(new Object()); + } }