8015809: More user friendly compile-time errors for uncaught exceptions in lambda expression
authorjlahoda
Thu, 15 Aug 2013 22:33:43 +0200
changeset 19502 f30b3ef165ea
parent 19501 9dac9369db2c
child 19503 58688e470d3b
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
langtools/src/share/classes/com/sun/tools/javac/code/Type.java
langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java
langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties
langtools/src/share/classes/com/sun/tools/javac/tree/JCTree.java
langtools/test/tools/javac/diags/examples/IncompatibleThrownTypesInLambda.java
langtools/test/tools/javac/lambda/ExceptionsInLambda.java
langtools/test/tools/javac/lambda/ExceptionsInLambda.out
langtools/test/tools/javac/lambda/TargetType21.out
--- 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