diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java index 4308fa98838..91273c5e086 100644 --- a/src/main/java/groovy/lang/MetaClassImpl.java +++ b/src/main/java/groovy/lang/MetaClassImpl.java @@ -3264,26 +3264,40 @@ private Object chooseMethodInternal(String methodName, Object methodOrList, Clas return doChooseMostSpecificParams(theClass.getName(), methodName, matchingMethods, arguments, false); } - protected static Object doChooseMostSpecificParams(String theClassName, String name, List matchingMethods, Class[] arguments, boolean checkParametersCompatible) { - long matchesDistance = -1; - LinkedList matches = new LinkedList(); + protected static Object doChooseMostSpecificParams(final String theClassName, final String name, final List matchingMethods, final Class[] arguments, final boolean checkParameterCompatibility) { + var matchesDistance = -1L; + var matches = new LinkedList<>(); for (Object method : matchingMethods) { - final ParameterTypes parameterTypes = (ParameterTypes) method; - if (checkParametersCompatible && !MetaClassHelper.parametersAreCompatible(arguments, parameterTypes.getNativeParameterTypes())) - continue; - long dist = MetaClassHelper.calculateParameterDistance(arguments, parameterTypes); - if (dist == 0) return method; - matchesDistance = handleMatches(matchesDistance, matches, method, dist); + var parameterTypes = (ParameterTypes) method; + if (!checkParameterCompatibility || MetaClassHelper.parametersAreCompatible(arguments, parameterTypes.getNativeParameterTypes())) { + long dist = MetaClassHelper.calculateParameterDistance(arguments, parameterTypes); + matchesDistance = handleMatches(matchesDistance, matches, method, dist); + } } int size = matches.size(); - if (size == 1) { - return matches.getFirst(); + if (size > 1) { // GROOVY-11258 + for (var iter = matches.iterator(); iter.hasNext(); ) { + Object outer = iter.next(); // if redundant, remove + for (Object inner : matches) { + if (inner == outer) continue; + Class[] innerTypes = ((ParameterTypes) inner).getNativeParameterTypes(); + Class[] outerTypes = ((ParameterTypes) outer).getNativeParameterTypes(); + if (!Arrays.equals(innerTypes, outerTypes) && MetaClassHelper.parametersAreCompatible(innerTypes, outerTypes)) { + iter.remove(); // for the given argument type(s), inner can accept everything that outer can + size -= 1; + break; + } + } + } } + if (size == 0) { return null; } - // more than one matching method found --> ambiguous! + if (size == 1) { + return matches.getFirst(); + } throw new GroovyRuntimeException(createErrorMessageForAmbiguity(theClassName, name, arguments, matches)); } diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java index 286283a4eca..bcf521910c1 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java @@ -1068,6 +1068,19 @@ private static List chooseBestMethods(final ClassNode receiver, Coll bestMethods.add(method); } } + if (bestMethods.size() > 1) { // GROOVY-11258 + for (var iter = bestMethods.iterator(); iter.hasNext(); ) { + MethodNode outer = iter.next(); // if redundant, remove + for (MethodNode inner : bestMethods) { + if (inner != outer + && !ParameterUtils.parametersEqual(inner.getParameters(), outer.getParameters()) + && ParameterUtils.parametersCompatible(inner.getParameters(), outer.getParameters())) { + iter.remove(); // for the given argument type(s), inner can accept everything that outer can + break; + } + } + } + } // phase 2: receiver-provider distance classifier if (bestMethods.size() > 1 && receiver != null) { diff --git a/src/test/groovy/ListTest.groovy b/src/test/groovy/ListTest.groovy index d20ef0773fd..b42300838b6 100644 --- a/src/test/groovy/ListTest.groovy +++ b/src/test/groovy/ListTest.groovy @@ -336,13 +336,13 @@ class ListTest extends GroovyTestCase { assert list[-1..-2] == [3, 2] , 'negative index range reversed' assert list[-1..<-2] == [3] , 'negative index range reversed right exclusive' assert list[-1<..-2] == [2] , 'negative index range reversed left exclusive' - assert list[-1<..<-2] == [] , 'negative index range reversed full exclusive' // aaaaaaahhhhh ! + assert list[-1<..<-2] == [] , 'negative index range reversed full exclusive' assert list[0..-1] == list , 'pos - neg value' assert list[0..<-1] == [0, 1, 2] , 'pos - neg value right exclusive' assert list[0<..-1] == [1, 2, 3] , 'pos - neg value left exclusive' assert list[0<..<-1] == [1, 2] , 'pos - neg value full exclusive' assert list[0..<-2] == [0, 1] , 'pos - neg value exclusive' - shouldFail(GroovyRuntimeException) { list[null] } + shouldFail(NullPointerException) { list[null] } shouldFail(IndexOutOfBoundsException) { list[5..6] } } diff --git a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy index 3d394de6b79..9abbbd5fd5c 100644 --- a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy +++ b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy @@ -867,8 +867,25 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase { } } - // GROOVY-11397 + // GROOVY-11258 void testSAMsInMethodSelection7() { + assertScript '''import java.util.function.Function + // org.assertj.core.api.iterable.ThrowingExtractor + interface F extends Function { + default Out apply(In it) { redir(it) } + Out redir(In it) throws Exception + } + class C { + Number m(Function f) { f.apply(1) } + Number m(F f) { f.redir(2) } + } + Number which = new C().m { it } + assert which == 2 + ''' + } + + // GROOVY-11397 + void testSAMsInMethodSelection8() { assertScript ''' interface Action { void run(T t) } interface Proc { void doSomething() }