Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephan Herrmann2014-03-08 20:25:56 +0000
committerStephan Herrmann2014-03-09 14:13:30 +0000
commitb1cdfe3ee438c0a38b27b1a4134346d549384d6a (patch)
tree3460cd75bf2c7d004eb9918d839d10ea61f271f8
parent4ec3d8941998f287d44af5beff0b7f8ef9d972cc (diff)
downloadeclipse.jdt.core-b1cdfe3ee438c0a38b27b1a4134346d549384d6a.tar.gz
eclipse.jdt.core-b1cdfe3ee438c0a38b27b1a4134346d549384d6a.tar.xz
eclipse.jdt.core-b1cdfe3ee438c0a38b27b1a4134346d549384d6a.zip
Bug 429430 - [1.8] Lambdas and method reference infer wrong exception
type with generics (RuntimeException instead of IOException)
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java101
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java2
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java20
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java2
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java13
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThrowStatement.java4
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintExceptionFormula.java6
7 files changed, 139 insertions, 9 deletions
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java
index a20de230ca..a8bc837761 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java
@@ -17,6 +17,7 @@ package org.eclipse.jdt.core.tests.compiler.regression;
import java.util.Map;
import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import junit.framework.Test;
@@ -2587,6 +2588,7 @@ public void testBug428811() {
"The method copyOf(Collection<T>) from the type MoreCollectors.ImmutableList<T> is never used locally\n" +
"----------\n");
}
+// all exceptions can be inferred to match
public void testBug429430() {
runConformTest(
new String[] {
@@ -2603,14 +2605,105 @@ public void testBug429430() {
"\n" +
" public static void main(String[] args) throws IOException {\n" +
" InputStream in = new ByteArrayInputStream(\"hello\".getBytes());\n" +
-// FIXME
-// " close( x -> x.close(), in ); // eclipse: NO, javac: YES\n" +
- " close( InputStream::close, in ); // eclipse: NO, javac: YES\n" +
- " close( (Closer<InputStream, IOException>)InputStream::close, in ); // eclipse: YES, javac: YES\n" +
+ " close( x -> x.close(), in );\n" +
+ " close( InputStream::close, in );\n" +
+ " close( (Closer<InputStream, IOException>)InputStream::close, in );\n" +
" }\n" +
"}\n"
});
}
+// incompatible exceptions prevent suitable inference of exception type
+public void testBug429430a() {
+ runNegativeTest(
+ new String[] {
+ "Main.java",
+ "import java.io.*;\n" +
+ "@SuppressWarnings(\"serial\") class EmptyStream extends Exception {}\n" +
+ "public class Main {\n" +
+ " public static interface Closer<T, X extends Exception> {\n" +
+ " void closeIt(T it) throws X;\n" +
+ " }\n" +
+ "\n" +
+ " public static <T, X extends Exception> void close( Closer<T, X> closer, T it ) throws X {\n" +
+ " closer.closeIt(it);\n" +
+ " }\n" +
+ "\n" +
+ " public static void main(String[] args) throws IOException, EmptyStream {\n" +
+ " InputStream in = new ByteArrayInputStream(\"hello\".getBytes());\n" +
+ " close( x -> { if (in.available() == 0) throw new EmptyStream(); x.close(); }, in );\n" +
+ " }\n" +
+ "}\n"
+ },
+ "----------\n" +
+ "1. ERROR in Main.java (at line 14)\n" +
+ " close( x -> { if (in.available() == 0) throw new EmptyStream(); x.close(); }, in );\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Unhandled exception type Exception\n" +
+ "----------\n");
+}
+// one of two incompatible exceptions is caught
+// FIXME: should be possible to infer X to EmptyStream
+public void _testBug429430b() {
+ runConformTest(
+ new String[] {
+ "Main.java",
+ "import java.io.*;\n" +
+ "@SuppressWarnings(\"serial\") class EmptyStream extends Exception {}\n" +
+ "public class Main {\n" +
+ " public static interface Closer<T, X extends Exception> {\n" +
+ " void closeIt(T it) throws X;\n" +
+ " }\n" +
+ "\n" +
+ " public static <T, X extends Exception> void close( Closer<T, X> closer, T it ) throws X {\n" +
+ " closer.closeIt(it);\n" +
+ " }\n" +
+ "\n" +
+ " public static void main(String[] args) throws EmptyStream {\n" +
+ " InputStream in = new ByteArrayInputStream(\"hello\".getBytes());\n" +
+ " close( x -> {\n" +
+ " try {\n" +
+ " x.close();\n" +
+ " } catch (IOException ioex) { throw new EmptyStream(); } \n" +
+ " }," +
+ " in);\n" +
+ " }\n" +
+ "}\n"
+ });
+}
+// ensure type annotation on exception doesn't confuse the inference
+public void testBug429430c() {
+ Map options = getCompilerOptions();
+ options.put(CompilerOptions.OPTION_Store_Annotations, CompilerOptions.ENABLED);
+ runConformTest(
+ new String[] {
+ "Main.java",
+ "import java.io.*;\n" +
+ "import java.lang.annotation.*;\n" +
+ "@Target(ElementType.TYPE_USE) @interface Severe {}\n" +
+ "public class Main {\n" +
+ " public static interface Closer<T, X extends Exception> {\n" +
+ " void closeIt(T it) throws X;\n" +
+ " }\n" +
+ "\n" +
+ " public static <T, X extends Exception> void close( Closer<T, X> closer, T it ) throws X {\n" +
+ " closer.closeIt(it);\n" +
+ " }\n" +
+ "\n" +
+ " static @Severe IOException getException() { return new IOException(\"severe\"); }\n" +
+ " public static void main(String[] args) throws IOException {\n" +
+ " InputStream in = new ByteArrayInputStream(\"hello\".getBytes());\n" +
+ " close( x -> {\n" +
+ " if (in.available() > 0)\n" +
+ " x.close();\n" +
+ " else\n" +
+ " throw getException();\n" +
+ " },\n" +
+ " in);\n" +
+ " }\n" +
+ "}\n"
+ },
+ options);
+}
public void testBug429490() {
runConformTest(
new String[] {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java
index 4d9bbcf0b5..95337ade92 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java
@@ -39,6 +39,7 @@
* Bug 426996 - [1.8][inference] try to avoid method Expression.unresolve()?
* Bug 428352 - [1.8][compiler] Resolution errors don't always surface
* Bug 429203 - [1.8][compiler] NPE in AllocationExpression.binding
+ * Bug 429430 - [1.8] Lambdas and method reference infer wrong exception type with generics (RuntimeException instead of IOException)
* Jesper S Moller <jesper@selskabet.org> - Contributions for
* bug 378674 - "The method can be declared as static" is wrong
* Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
@@ -555,6 +556,7 @@ TypeBinding resolvePart3(ResolutionState state) {
new ImplicitNullAnnotationVerifier(state.scope.environment(), compilerOptions.inheritNullAnnotations)
.checkImplicitNullAnnotations(this.binding, null/*srcMethod*/, false, state.scope);
}
+ recordExceptionsForEnclosingLambda(state.scope, this.binding.thrownExceptions);
return allocationType;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
index acfed80b45..f1276bc85f 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
@@ -33,6 +33,7 @@
* Bug 428294 - [1.8][compiler] Type mismatch: cannot convert from List<Object> to Collection<Object[]>
* Bug 428786 - [1.8][compiler] Inference needs to compute the "ground target type" when reducing a lambda compatibility constraint
* Bug 428980 - [1.8][null] simple expression as lambda body doesn't leverage null annotation on argument
+ * Bug 429430 - [1.8] Lambdas and method reference infer wrong exception type with generics (RuntimeException instead of IOException)
* Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
* Bug 405104 - [1.8][compiler][codegen] Implement support for serializeable lambdas
*******************************************************************************/
@@ -40,6 +41,8 @@ package org.eclipse.jdt.internal.compiler.ast;
import static org.eclipse.jdt.internal.compiler.ast.ExpressionContext.INVOCATION_CONTEXT;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
@@ -114,6 +117,7 @@ public class LambdaExpression extends FunctionalExpression implements ReferenceC
private boolean hasIgnoredMandatoryErrors = false;
private ReferenceBinding classType;
public int ordinal;
+ private Set thrownExceptions;
private static final SyntheticArgumentBinding [] NO_SYNTHETIC_ARGUMENTS = new SyntheticArgumentBinding[0];
private static final Block NO_BODY = new Block(0, true);
@@ -1045,6 +1049,20 @@ public class LambdaExpression extends FunctionalExpression implements ReferenceC
}
}
+ public void throwsException(TypeBinding exceptionType) {
+ if (this.expressionContext != INVOCATION_CONTEXT)
+ return;
+ if (this.thrownExceptions == null)
+ this.thrownExceptions = new HashSet<TypeBinding>();
+ this.thrownExceptions.add(exceptionType);
+ }
+
+ public Set<TypeBinding> getThrownExceptions() {
+ if (this.thrownExceptions == null)
+ return Collections.emptySet();
+ return this.thrownExceptions;
+ }
+
public void generateCode(ClassScope classScope, ClassFile classFile) {
int problemResetPC = 0;
classFile.codeStream.wideMode = false;
@@ -1244,4 +1262,4 @@ public class LambdaExpression extends FunctionalExpression implements ReferenceC
}
return this.classType = new LambdaTypeBinding();
}
-} \ No newline at end of file
+}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
index 2cd3ff7b18..d6815b7071 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
@@ -50,6 +50,7 @@
* Bug 427438 - [1.8][compiler] NPE at org.eclipse.jdt.internal.compiler.ast.ConditionalExpression.generateCode(ConditionalExpression.java:280)
* Bug 426996 - [1.8][inference] try to avoid method Expression.unresolve()?
* Bug 428352 - [1.8][compiler] Resolution errors don't always surface
+ * Bug 429430 - [1.8] Lambdas and method reference infer wrong exception type with generics (RuntimeException instead of IOException)
* Jesper S Moller - Contributions for
* Bug 378674 - "The method can be declared as static" is wrong
* Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
@@ -853,6 +854,7 @@ public TypeBinding resolveType(BlockScope scope) {
if (this.typeArguments != null && this.binding.original().typeVariables == Binding.NO_TYPE_VARIABLES) {
scope.problemReporter().unnecessaryTypeArgumentsForMethodInvocation(this.binding, this.genericTypeArguments, this.typeArguments);
}
+ recordExceptionsForEnclosingLambda(scope, this.binding.thrownExceptions);
return (this.resolvedType.tagBits & TagBits.HasMissingType) == 0
? this.resolvedType
: null;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java
index dcc8349374..9f3a2da65d 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java
@@ -32,6 +32,7 @@
* Bug 424415 - [1.8][compiler] Eventual resolution of ReferenceExpression is not seen to be happening.
* Bug 418537 - [1.8][null] Fix null type annotation analysis for poly conditional expressions
* Bug 428352 - [1.8][compiler] Resolution errors don't always surface
+ * Bug 429430 - [1.8] Lambdas and method reference infer wrong exception type with generics (RuntimeException instead of IOException)
* Andy Clement - Contributions for
* Bug 383624 - [1.8][compiler] Revive code generation support for type annotations (from Olivier's work)
* Bug 409250 - [1.8][compiler] Various loose ends in 308 code generation
@@ -366,4 +367,16 @@ protected MethodBinding findConstructorBinding(BlockScope scope, Invocation site
resolvePolyExpressionArguments(site, ctorBinding, argumentTypes, scope);
return ctorBinding;
}
+/**
+ * If an exception-throwing statement is resolved within the scope of a lambda, record the exception type(s).
+ * It is likely wrong to do this during resolve, should probably use precise flow information.
+ */
+protected void recordExceptionsForEnclosingLambda(BlockScope scope, TypeBinding... thrownExceptions) {
+ MethodScope methodScope = scope.methodScope();
+ if (methodScope != null && methodScope.referenceContext instanceof LambdaExpression) {
+ LambdaExpression lambda = (LambdaExpression) methodScope.referenceContext;
+ for (int i = 0; i < thrownExceptions.length; i++)
+ lambda.throwsException(thrownExceptions[i]);
+ }
+}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThrowStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThrowStatement.java
index ea45c4e5e8..3e3ec22191 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThrowStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThrowStatement.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2013 IBM Corporation and others.
+ * Copyright (c) 2000, 2014 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -15,6 +15,7 @@
* bug 359334 - Analysis for resource leak warnings does not consider exceptions as method exit points
* bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
+ * Bug 429430 - [1.8] Lambdas and method reference infer wrong exception type with generics (RuntimeException instead of IOException)
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -71,6 +72,7 @@ public StringBuffer printStatement(int indent, StringBuffer output) {
public void resolve(BlockScope scope) {
this.exceptionType = this.exception.resolveType(scope);
+ recordExceptionsForEnclosingLambda(scope, this.exceptionType);
if (this.exceptionType != null && this.exceptionType.isValidBinding()) {
if (this.exceptionType == TypeBinding.NULL) {
if (scope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3){
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintExceptionFormula.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintExceptionFormula.java
index f1590d786a..3e96f09f42 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintExceptionFormula.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintExceptionFormula.java
@@ -76,9 +76,9 @@ public class ConstraintExceptionFormula extends ConstraintFormula {
e[n++] = (InferenceVariable) thrown[i]; // thrown[i] is not a proper type, since it's an exception it must be an inferenceVariable, right?
TypeBinding[] ePrime = null;
if (this.left instanceof LambdaExpression) {
-// TODO find exceptions thrown by the lambda's body, see 18.2.5 bullet 5
-// ((LambdaExpression)this.left).
-// InferenceContext18.missingImplementation("NYI");
+ LambdaExpression lambda = ((LambdaExpression) this.left).getResolvedCopyForInferenceTargeting(this.right);
+ Set<TypeBinding> ePrimeSet = lambda.getThrownExceptions();
+ ePrime = ePrimeSet.toArray(new TypeBinding[ePrimeSet.size()]);
} else {
ReferenceExpression referenceExpression = (ReferenceExpression)this.left;
MethodBinding method = referenceExpression.findCompileTimeMethodTargeting(this.right, scope);

Back to the top