diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java index 7e5c6b6..b6a5ef1 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java @@ -4017,6 +4017,89 @@ }, "null"); } +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=430310, [1.8][compiler] Functional interface incorrectly rejected as not being. +public void test430310() { + this.runConformTest( + new String[] { + "X.java", + "interface Func1 {\n" + + " R apply(T1 v1);\n" + + " void other();\n" + + "}\n" + + "@FunctionalInterface // spurious error: F1 is not a functional interface\n" + + "public interface X extends Func1 {\n" + + " default void other() {}\n" + + " public static void main(String [] args) {\n" + + " System.out.println(\"OK\");\n" + + " }\n" + + "}\n" + }, + "OK"); +} +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=430310, [1.8][compiler] Functional interface incorrectly rejected as not being. +public void test430310a() { + this.runConformTest( + new String[] { + "X.java", + "@FunctionalInterface\n" + + "public interface X {\n" + + " R apply(T1 v1, T2 v2);\n" + + " default void other() {}\n" + + " public static void main(String[] args) {\n" + + " System.out.println(\"OK\");\n" + + " }\n" + + "}\n" + }, + "OK"); +} +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=430310, [1.8][compiler] Functional interface incorrectly rejected as not being. +public void test430310b() { + this.runConformTest( + new String[] { + "X.java", + "interface I1 {\n" + + " int foo(String s);\n" + + "}\n" + + "@FunctionalInterface\n" + + "interface A1 extends I1 {\n" + + " @Override\n" + + " default int foo(String s) {\n" + + " return -1;\n" + + " }\n" + + " int foo(java.io.Serializable s);\n" + + "}\n" + + "public class X {\n" + + " public static void main(String[] args) {\n" + + " System.out.println(\"OK\");\n" + + " }\n" + + "}\n" + }, + "OK"); +} +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=430310, [1.8][compiler] Functional interface incorrectly rejected as not being. +public void test430310c() { + this.runConformTest( + new String[] { + "X.java", + "interface I2 {\n" + + " int foo(String s);\n" + + "}\n" + + "@FunctionalInterface\n" + + "interface A2 extends I2 {\n" + + " @Override\n" + + " default int foo(String s) {\n" + + " return -1;\n" + + " }\n" + + " int bar(java.io.Serializable s);\n" + + "}\n" + + "public class X {\n" + + " public static void main(String[] args) {\n" + + " System.out.println(\"OK\");\n" + + " }\n" + + "}\n" + }, + "OK"); +} public static Class testClass() { return LambdaExpressionsTest.class; diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java index 3727dbc..c53c769 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java @@ -8963,6 +8963,30 @@ "Unhandled exception type Exception\n" + "----------\n"); } +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=430310, [1.8][compiler] Functional interface incorrectly rejected as not being. +public void test430310() { + this.runNegativeTest( + new String[] { + "X.java", + "interface Func1 {\n" + + " R apply(T1 v1);\n" + + " void other();\n" + + "}\n" + + "@FunctionalInterface // spurious error: F1 is not a functional interface\n" + + "interface F1 extends Func1 {\n" + + " default void other() {}\n" + + "}\n" + + "@FunctionalInterface\n" + + "interface F2 extends Func1 {\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 10)\n" + + " interface F2 extends Func1 {\n" + + " ^^\n" + + "Invalid \'@FunctionalInterface\' annotation; F2 is not a functional interface\n" + + "----------\n"); +} public static Class testClass() { return NegativeLambdaExpressionsTest.class; } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java index 75933ff..d3a6ab0 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java @@ -1867,48 +1867,29 @@ int contractsCount = 0; int contractsLength = 0; - // -- the following are used for early termination. - MethodBinding aContract = null; - int contractParameterLength = 0; - char [] contractSelector = null; - // --- - ReferenceBinding [] superInterfaces = superInterfaces(); for (int i = 0, length = superInterfaces.length; i < length; i++) { MethodBinding [] superInterfaceContracts = superInterfaces[i].getInterfaceAbstractContracts(scope); final int superInterfaceContractsLength = superInterfaceContracts == null ? 0 : superInterfaceContracts.length; - if (superInterfaceContractsLength == 0) continue; - if (aContract == null) { - aContract = superInterfaceContracts[0]; - contractParameterLength = aContract.parameters.length; - contractSelector = aContract.selector; - contracts = superInterfaceContracts; - contractsCount = contractsLength = superInterfaceContractsLength; - } else { - if (superInterfaceContracts[0].parameters.length != contractParameterLength || !CharOperation.equals(contractSelector, superInterfaceContracts[0].selector)) { - throw new InvalidInputException("Not a functional interface"); //$NON-NLS-1$ - } - if (contractsLength < contractsCount + superInterfaceContractsLength) { - System.arraycopy(contracts, 0, contracts = new MethodBinding[contractsLength = contractsCount + superInterfaceContractsLength], 0, contractsCount); - } - System.arraycopy(superInterfaceContracts, 0, contracts, contractsCount, superInterfaceContractsLength); - contractsCount += superInterfaceContractsLength; + if (contractsLength < contractsCount + superInterfaceContractsLength) { + System.arraycopy(contracts, 0, contracts = new MethodBinding[contractsLength = contractsCount + superInterfaceContractsLength], 0, contractsCount); } + System.arraycopy(superInterfaceContracts, 0, contracts, contractsCount, superInterfaceContractsLength); + contractsCount += superInterfaceContractsLength; } + for (int i = 0, length = methods == null ? 0 : methods.length; i < length; i++) { final MethodBinding method = methods[i]; - if (method.isStatic() || method.redeclaresPublicObjectMethod(scope)) continue; + if (method == null || method.isStatic() || method.redeclaresPublicObjectMethod(scope)) + continue; + if (!method.isValidBinding()) + throw new InvalidInputException("Not a functional interface"); //$NON-NLS-1$ if (method.isDefaultMethod()) { for (int j = 0; j < contractsCount; j++) { if (contracts[j] == null) continue; if (MethodVerifier.doesMethodOverride(method, contracts[j], scope.environment())) { - if (aContract == contracts[j]) { - aContract = null; - contractParameterLength = 0; - contractSelector = null; - } contractsCount--; // abstract method from super type rendered default by present interface ==> contracts[j] = null; if (j < contractsCount) @@ -1916,16 +1897,6 @@ } } continue; // skip default method itself - } - final boolean validBinding = method.isValidBinding(); - if (aContract == null && validBinding) { - aContract = method; - contractParameterLength = aContract.parameters.length; - contractSelector = aContract.selector; - } else { - if (!validBinding || method.parameters.length != contractParameterLength || !CharOperation.equals(contractSelector, method.selector)) { - throw new InvalidInputException("Not a functional interface"); //$NON-NLS-1$ - } } if (contractsCount == contractsLength) { System.arraycopy(contracts, 0, contracts = new MethodBinding[contractsLength += 16], 0, contractsCount); @@ -1952,15 +1923,32 @@ MethodBinding[] methods = null; try { methods = getInterfaceAbstractContracts(scope); + if (methods == null || methods.length == 0) + return this.singleAbstractMethod[index] = samProblemBinding; + int contractParameterLength = 0; + char [] contractSelector = null; + for (int i = 0, length = methods.length; i < length; i++) { + MethodBinding method = methods[i]; + if (method == null) continue; + if (contractSelector == null) { + contractSelector = method.selector; + contractParameterLength = method.parameters == null ? 0 : method.parameters.length; + } else { + int methodParameterLength = method.parameters == null ? 0 : method.parameters.length; + if (methodParameterLength != contractParameterLength || !CharOperation.equals(method.selector, contractSelector)) + return this.singleAbstractMethod[index] = samProblemBinding; + } + } } catch (InvalidInputException e) { return this.singleAbstractMethod[index] = samProblemBinding; } - if (methods != null && methods.length == 1) + if (methods.length == 1) return this.singleAbstractMethod[index] = methods[0]; final LookupEnvironment environment = scope.environment(); boolean genericMethodSeen = false; int length = methods.length; + next:for (int i = length - 1; i >= 0; --i) { MethodBinding method = methods[i], otherMethod = null; if (method.typeVariables != Binding.NO_TYPE_VARIABLES)