8015809: More user friendly compile-time errors for uncaught exceptions in lambda expression
Summary: Producing individual errors for uncaught undeclared exceptions inside lambda expressions, rather than one error for the whole lambda
Reviewed-by: mcimadamore
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Type.java Fri Aug 16 10:32:42 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Type.java Thu Aug 15 22:33:43 2013 +0200
@@ -1161,7 +1161,7 @@
}
public boolean contains(Type elem) {
- return elem == this || contains(argtypes, elem) || restype.contains(elem);
+ return elem == this || contains(argtypes, elem) || restype.contains(elem) || contains(thrown, elem);
}
public MethodType asMethodType() { return this; }
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java Fri Aug 16 10:32:42 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java Thu Aug 15 22:33:43 2013 +0200
@@ -2607,8 +2607,7 @@
* are compatible with the expected functional interface descriptor. This means that:
* (i) parameter types must be identical to those of the target descriptor; (ii) return
* types must be compatible with the return type of the expected descriptor;
- * (iii) thrown types must be 'included' in the thrown types list of the expected
- * descriptor.
+ * (iii) finish inference of thrown types if required.
*/
private void checkLambdaCompatible(JCLambda tree, Type descriptor, CheckContext checkContext, boolean speculativeAttr) {
Type returnType = checkContext.inferenceContext().asFree(descriptor.getReturnType());
@@ -2630,9 +2629,7 @@
if (!speculativeAttr) {
List<Type> thrownTypes = checkContext.inferenceContext().asFree(descriptor.getThrownTypes());
- if (chk.unhandled(tree.inferredThrownTypes == null ? List.<Type>nil() : tree.inferredThrownTypes, thrownTypes).nonEmpty()) {
- log.error(tree, "incompatible.thrown.types.in.lambda", tree.inferredThrownTypes);
- }
+ chk.unhandled(tree.inferredThrownTypes == null ? List.<Type>nil() : tree.inferredThrownTypes, thrownTypes);
}
}
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java Fri Aug 16 10:32:42 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java Thu Aug 15 22:33:43 2013 +0200
@@ -224,7 +224,7 @@
}
try {
new AliveAnalyzer().analyzeTree(env, that, make);
- new FlowAnalyzer().analyzeTree(env, that, make);
+ new LambdaFlowAnalyzer().analyzeTree(env, that, make);
} finally {
if (!speculative) {
log.popDiagnosticHandler(diagHandler);
@@ -1259,12 +1259,24 @@
ListBuffer<FlowPendingExit> prevPending = pendingExits;
try {
pendingExits = ListBuffer.lb();
- caught = List.of(syms.throwableType); //inhibit exception checking
+ caught = tree.getDescriptorType(types).getThrownTypes();
thrown = List.nil();
scan(tree.body);
- tree.inferredThrownTypes = thrown;
- }
- finally {
+ List<FlowPendingExit> exits = pendingExits.toList();
+ pendingExits = new ListBuffer<FlowPendingExit>();
+ while (exits.nonEmpty()) {
+ FlowPendingExit exit = exits.head;
+ exits = exits.tail;
+ if (exit.thrown == null) {
+ Assert.check(exit.tree.hasTag(RETURN));
+ } else {
+ // uncaught throws will be reported later
+ pendingExits.append(exit);
+ }
+ }
+
+ errorUncaught();
+ } finally {
pendingExits = prevPending;
caught = prevCaught;
thrown = prevThrown;
@@ -1303,6 +1315,33 @@
}
/**
+ * Specialized pass that performs inference of thrown types for lambdas.
+ */
+ class LambdaFlowAnalyzer extends FlowAnalyzer {
+ @Override
+ public void visitLambda(JCLambda tree) {
+ if (tree.type != null &&
+ tree.type.isErroneous()) {
+ return;
+ }
+ List<Type> prevCaught = caught;
+ List<Type> prevThrown = thrown;
+ ListBuffer<FlowPendingExit> prevPending = pendingExits;
+ try {
+ pendingExits = ListBuffer.lb();
+ caught = List.of(syms.throwableType);
+ thrown = List.nil();
+ scan(tree.body);
+ tree.inferredThrownTypes = thrown;
+ } finally {
+ pendingExits = prevPending;
+ caught = prevCaught;
+ thrown = prevThrown;
+ }
+ }
+ }
+
+ /**
* This pass implements (i) definite assignment analysis, which ensures that
* each variable is assigned when used and (ii) definite unassignment analysis,
* which ensures that no final variable is assigned more than once. This visitor
--- a/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties Fri Aug 16 10:32:42 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties Thu Aug 15 22:33:43 2013 +0200
@@ -733,10 +733,6 @@
{0}
# 0: list of type
-compiler.err.incompatible.thrown.types.in.lambda=\
- incompatible thrown types {0} in lambda expression
-
-# 0: list of type
compiler.err.incompatible.thrown.types.in.mref=\
incompatible thrown types {0} in method reference
--- a/langtools/src/share/classes/com/sun/tools/javac/tree/JCTree.java Fri Aug 16 10:32:42 2013 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/tree/JCTree.java Thu Aug 15 22:33:43 2013 +0200
@@ -645,7 +645,7 @@
public List<Type> targets;
public Type getDescriptorType(Types types) {
- return types.findDescriptorType(targets.head);
+ return targets.nonEmpty() ? types.findDescriptorType(targets.head) : types.createErrorType(null);
}
}
--- a/langtools/test/tools/javac/diags/examples/IncompatibleThrownTypesInLambda.java Fri Aug 16 10:32:42 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-// key: compiler.err.incompatible.thrown.types.in.lambda
-
-class IncompatibleThrownTypesInLambda {
- interface SAM {
- void m();
- }
-
- SAM s = ()-> { throw new Exception(); };
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/ExceptionsInLambda.java Thu Aug 15 22:33:43 2013 +0200
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8015809
+ * @summary Producing individual errors for uncaught undeclared exceptions inside lambda expressions, instead of one error for whole lambda
+ * @compile/fail/ref=ExceptionsInLambda.out -XDrawDiagnostics ExceptionsInLambda.java
+ */
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.InputStream;
+import java.io.Reader;
+
+public class ExceptionsInLambda {
+
+ public static void main(Runnable p, File f) {
+ main(() -> {
+ StringBuilder sb = new StringBuilder();
+
+ Reader in = new FileReader(f);
+ int r;
+
+ while ((r = in.read()) != (-1)) {
+ sb.append((char) r);
+ }
+ }, f);
+
+ doOpen(() -> new FileInputStream(f));
+ }
+
+ public static InputStream doOpen(Open open) {
+ return open.open();
+ }
+
+ public interface Open {
+ public InputStream open();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/ExceptionsInLambda.out Thu Aug 15 22:33:43 2013 +0200
@@ -0,0 +1,4 @@
+ExceptionsInLambda.java:43:25: compiler.err.unreported.exception.need.to.catch.or.throw: java.io.FileNotFoundException
+ExceptionsInLambda.java:46:32: compiler.err.unreported.exception.need.to.catch.or.throw: java.io.IOException
+ExceptionsInLambda.java:51:22: compiler.err.unreported.exception.need.to.catch.or.throw: java.io.FileNotFoundException
+3 errors
--- a/langtools/test/tools/javac/lambda/TargetType21.out Fri Aug 16 10:32:42 2013 +0100
+++ b/langtools/test/tools/javac/lambda/TargetType21.out Thu Aug 15 22:33:43 2013 +0200
@@ -1,6 +1,5 @@
TargetType21.java:28:9: compiler.err.ref.ambiguous: call, kindname.method, call(TargetType21.SAM2), TargetType21, kindname.method, <R,A>call(TargetType21.SAM3<R,A>), TargetType21
-TargetType21.java:28:14: compiler.err.incompatible.thrown.types.in.lambda: java.lang.Exception
TargetType21.java:29:9: compiler.err.ref.ambiguous: call, kindname.method, call(TargetType21.SAM2), TargetType21, kindname.method, <R,A>call(TargetType21.SAM3<R,A>), TargetType21
TargetType21.java:30:13: compiler.err.prob.found.req: (compiler.misc.cyclic.inference: A)
TargetType21.java:31:9: compiler.err.ref.ambiguous: call, kindname.method, call(TargetType21.SAM1), TargetType21, kindname.method, <R,A>call(TargetType21.SAM3<R,A>), TargetType21
-5 errors
+4 errors