From 16891a92f6e7ce9df9275fa775106e5470b033de Mon Sep 17 00:00:00 2001 From: jlahoda Date: Thu, 15 Aug 2013 22:33:43 +0200 Subject: [PATCH] 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 --- .../com/sun/tools/javac/code/Type.java | 2 +- .../com/sun/tools/javac/comp/Attr.java | 7 +-- .../com/sun/tools/javac/comp/Flow.java | 49 +++++++++++++++++-- .../tools/javac/resources/compiler.properties | 4 -- .../com/sun/tools/javac/tree/JCTree.java | 2 +- .../ExceptionsInLambda.java} | 41 +++++++++++++--- .../tools/javac/lambda/ExceptionsInLambda.out | 4 ++ test/tools/javac/lambda/TargetType21.out | 3 +- 8 files changed, 88 insertions(+), 24 deletions(-) rename test/tools/javac/{diags/examples/IncompatibleThrownTypesInLambda.java => lambda/ExceptionsInLambda.java} (50%) create mode 100644 test/tools/javac/lambda/ExceptionsInLambda.out diff --git a/src/share/classes/com/sun/tools/javac/code/Type.java b/src/share/classes/com/sun/tools/javac/code/Type.java index 9384f43c..fb6308ba 100644 --- a/src/share/classes/com/sun/tools/javac/code/Type.java +++ b/src/share/classes/com/sun/tools/javac/code/Type.java @@ -1161,7 +1161,7 @@ public abstract class Type implements TypeMirror { } 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; } diff --git a/src/share/classes/com/sun/tools/javac/comp/Attr.java b/src/share/classes/com/sun/tools/javac/comp/Attr.java index 955880c4..5a0a2edf 100644 --- a/src/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java @@ -2607,8 +2607,7 @@ public class Attr extends JCTree.Visitor { * 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 @@ public class Attr extends JCTree.Visitor { if (!speculativeAttr) { List thrownTypes = checkContext.inferenceContext().asFree(descriptor.getThrownTypes()); - if (chk.unhandled(tree.inferredThrownTypes == null ? List.nil() : tree.inferredThrownTypes, thrownTypes).nonEmpty()) { - log.error(tree, "incompatible.thrown.types.in.lambda", tree.inferredThrownTypes); - } + chk.unhandled(tree.inferredThrownTypes == null ? List.nil() : tree.inferredThrownTypes, thrownTypes); } } diff --git a/src/share/classes/com/sun/tools/javac/comp/Flow.java b/src/share/classes/com/sun/tools/javac/comp/Flow.java index 10ff59c4..e488d45b 100644 --- a/src/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/share/classes/com/sun/tools/javac/comp/Flow.java @@ -224,7 +224,7 @@ public class Flow { } 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 @@ public class Flow { ListBuffer 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 exits = pendingExits.toList(); + pendingExits = new ListBuffer(); + 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; @@ -1302,6 +1314,33 @@ public class Flow { } } + /** + * 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 prevCaught = caught; + List prevThrown = thrown; + ListBuffer 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, diff --git a/src/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/share/classes/com/sun/tools/javac/resources/compiler.properties index 908b0aff..9361be25 100644 --- a/src/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -732,10 +732,6 @@ compiler.misc.incompatible.ret.type.in.mref=\ bad return type in method reference\n\ {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 diff --git a/src/share/classes/com/sun/tools/javac/tree/JCTree.java b/src/share/classes/com/sun/tools/javac/tree/JCTree.java index 313596a3..d2e9b673 100644 --- a/src/share/classes/com/sun/tools/javac/tree/JCTree.java +++ b/src/share/classes/com/sun/tools/javac/tree/JCTree.java @@ -645,7 +645,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { public List targets; public Type getDescriptorType(Types types) { - return types.findDescriptorType(targets.head); + return targets.nonEmpty() ? types.findDescriptorType(targets.head) : types.createErrorType(null); } } diff --git a/test/tools/javac/diags/examples/IncompatibleThrownTypesInLambda.java b/test/tools/javac/lambda/ExceptionsInLambda.java similarity index 50% rename from test/tools/javac/diags/examples/IncompatibleThrownTypesInLambda.java rename to test/tools/javac/lambda/ExceptionsInLambda.java index 53845226..bb63dae8 100644 --- a/test/tools/javac/diags/examples/IncompatibleThrownTypesInLambda.java +++ b/test/tools/javac/lambda/ExceptionsInLambda.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -21,12 +21,41 @@ * questions. */ -// key: compiler.err.incompatible.thrown.types.in.lambda +/* + * @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(); -class IncompatibleThrownTypesInLambda { - interface SAM { - void m(); + Reader in = new FileReader(f); + int r; + + while ((r = in.read()) != (-1)) { + sb.append((char) r); + } + }, f); + + doOpen(() -> new FileInputStream(f)); } - SAM s = ()-> { throw new Exception(); }; + public static InputStream doOpen(Open open) { + return open.open(); + } + + public interface Open { + public InputStream open(); + } } diff --git a/test/tools/javac/lambda/ExceptionsInLambda.out b/test/tools/javac/lambda/ExceptionsInLambda.out new file mode 100644 index 00000000..5cfa2491 --- /dev/null +++ b/test/tools/javac/lambda/ExceptionsInLambda.out @@ -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 diff --git a/test/tools/javac/lambda/TargetType21.out b/test/tools/javac/lambda/TargetType21.out index 1dd50782..904e30f1 100644 --- a/test/tools/javac/lambda/TargetType21.out +++ b/test/tools/javac/lambda/TargetType21.out @@ -1,6 +1,5 @@ TargetType21.java:28:9: compiler.err.ref.ambiguous: call, kindname.method, call(TargetType21.SAM2), TargetType21, kindname.method, call(TargetType21.SAM3), 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, call(TargetType21.SAM3), 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, call(TargetType21.SAM3), TargetType21 -5 errors +4 errors -- GitLab