diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index ad0369bf908c..58218357921f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -1022,6 +1022,9 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh } } } + if (!myHolder.hasErrorResults()) { + myHolder.add(HighlightUtil.checkUnhandledExceptions(expression, expression.getTextRange())); + } } @Override diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/SurroundWithTryCatchFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/SurroundWithTryCatchFix.java index a64fd51e8e61..97dd2ce2f339 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/SurroundWithTryCatchFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/SurroundWithTryCatchFix.java @@ -38,10 +38,16 @@ import org.jetbrains.annotations.NotNull; public class SurroundWithTryCatchFix implements IntentionAction { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.SurroundWithTryCatchFix"); - private PsiStatement myStatement; + private PsiStatement myStatement = null; public SurroundWithTryCatchFix(PsiElement element) { - myStatement = PsiTreeUtil.getNonStrictParentOfType(element, PsiStatement.class); + final PsiMethodReferenceExpression methodReferenceExpression = PsiTreeUtil.getParentOfType(element, PsiMethodReferenceExpression.class, false); + if (methodReferenceExpression == null) { + final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(element, PsiLambdaExpression.class); + if (lambdaExpression == null || lambdaExpression.getBody() instanceof PsiCodeBlock) { + myStatement = PsiTreeUtil.getNonStrictParentOfType(element, PsiStatement.class); + } + } } @Override diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java b/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java index 1707915b8804..e21d2cda20f4 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/ExceptionUtil.java @@ -240,6 +240,9 @@ public class ExceptionUtil { PsiCallExpression expression = (PsiCallExpression)element; unhandledExceptions = getUnhandledExceptions(expression, topElement, includeSelfCalls); } + else if (element instanceof PsiMethodReferenceExpression) { + unhandledExceptions = getUnhandledExceptions((PsiMethodReferenceExpression)element, topElement); + } else if (element instanceof PsiThrowStatement) { PsiThrowStatement statement = (PsiThrowStatement)element; unhandledExceptions = getUnhandledExceptions(statement, topElement); @@ -310,6 +313,16 @@ public class ExceptionUtil { return foundExceptions; } + private static Collection getUnhandledExceptions(PsiMethodReferenceExpression methodReferenceExpression, + PsiElement topElement) { + final JavaResolveResult resolveResult = methodReferenceExpression.advancedResolve(false); + final PsiElement resolve = resolveResult.getElement(); + if (resolve instanceof PsiMethod) { + return getUnhandledExceptions((PsiMethod)resolve, methodReferenceExpression, topElement, resolveResult.getSubstitutor()); + } + return Collections.emptyList(); + } + private static boolean firstStatementIsConstructorCall(PsiCodeBlock constructorBody) { final PsiStatement[] statements = constructorBody.getStatements(); if (statements.length == 0) return false; @@ -337,6 +350,12 @@ public class ExceptionUtil { visitElement(statement); } + @Override + public void visitMethodReferenceExpression(PsiMethodReferenceExpression expression) { + addExceptions(array, getUnhandledExceptions(expression, null)); + visitElement(expression); + } + @Override public void visitResourceVariable(PsiResourceVariable resourceVariable) { addExceptions(array, getUnhandledCloserExceptions(resourceVariable, null)); @@ -472,12 +491,17 @@ public class ExceptionUtil { } private static boolean isArrayClone(PsiMethod method, PsiElement element) { - if (!(element instanceof PsiMethodCallExpression)) return false; if (!method.getName().equals(CLONE_METHOD_NAME)) return false; PsiClass containingClass = method.getContainingClass(); if (containingClass == null || !CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName())) { return false; } + if (element instanceof PsiMethodReferenceExpression) { + final PsiMethodReferenceExpression methodCallExpression = (PsiMethodReferenceExpression)element; + final PsiExpression qualifierExpression = methodCallExpression.getQualifierExpression(); + return qualifierExpression != null && qualifierExpression.getType() instanceof PsiArrayType; + } + if (!(element instanceof PsiMethodCallExpression)) return false; PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)element; final PsiExpression qualifierExpression = methodCallExpression.getMethodExpression().getQualifierExpression(); @@ -545,7 +569,12 @@ public class ExceptionUtil { return parent instanceof PsiAnonymousClass && isHandled(parent, exceptionType, topElement); } else if (parent instanceof PsiLambdaExpression) { - return true; + final PsiType interfaceType = ((PsiLambdaExpression)parent).getFunctionalInterfaceType(); + return isDeclaredBySAMMethod(exceptionType, interfaceType); + } + else if (element instanceof PsiMethodReferenceExpression) { + final PsiType interfaceType = ((PsiMethodReferenceExpression)element).getFunctionalInterfaceType(); + return isDeclaredBySAMMethod(exceptionType, interfaceType); } else if (parent instanceof PsiClassInitializer) { if (((PsiClassInitializer)parent).hasModifierProperty(PsiModifier.STATIC)) return false; @@ -592,6 +621,16 @@ public class ExceptionUtil { return isHandled(parent, exceptionType, topElement); } + private static boolean isDeclaredBySAMMethod(PsiClassType exceptionType, PsiType interfaceType) { + if (interfaceType != null) { + final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(interfaceType); + if (interfaceMethod != null) { + return isHandledByMethodThrowsClause(interfaceMethod, exceptionType); + } + } + return true; + } + private static boolean areAllConstructorsThrow(final PsiClass aClass, PsiClassType exceptionType) { if (aClass == null) return false; final PsiMethod[] constructors = aClass.getConstructors(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/UnhandledExceptions.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/UnhandledExceptions.java new file mode 100644 index 000000000000..077940408e56 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/UnhandledExceptions.java @@ -0,0 +1,19 @@ +public class ExTest { + public static void maybeThrow(String data) throws Ex { + throw new Ex(data); + } + + { + Block b = (t) -> ExTest.maybeThrow(t); + } + + + private static class Ex extends Throwable { + public Ex(String s) { + } + } +} + +interface Block { + public void accept(T t); +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/UnhandledExceptions.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/UnhandledExceptions.java new file mode 100644 index 000000000000..a03154d9ecf4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/UnhandledExceptions.java @@ -0,0 +1,19 @@ +public class ExTest { + public static void maybeThrow(String data) throws Ex { + throw new Ex(data); + } + + { + Block b = ExTest::maybeThrow; + } + + + private static class Ex extends Throwable { + public Ex(String s) { + } + } +} + +interface Block { + public void accept(T t); +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithTry/afterLambdaCodeBlock.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithTry/afterLambdaCodeBlock.java new file mode 100644 index 000000000000..67eeb7469d52 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithTry/afterLambdaCodeBlock.java @@ -0,0 +1,26 @@ +// "Surround with try/catch" "true" +public class ExTest { + public static void maybeThrow(String data) throws Ex { + throw new Ex(data); + } + + { + Block b = (t) -> { + try { + return ExTest.maybeThrow(t); + } catch (Ex ex) { + ex.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + }; + } + + + private static class Ex extends Throwable { + public Ex(String s) { + } + } +} + +interface Block { + public void accept(T t); +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithTry/beforeLambdaCodeBlock.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithTry/beforeLambdaCodeBlock.java new file mode 100644 index 000000000000..1b296d021490 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithTry/beforeLambdaCodeBlock.java @@ -0,0 +1,20 @@ +// "Surround with try/catch" "true" +public class ExTest { + public static void maybeThrow(String data) throws Ex { + throw new Ex(data); + } + + { + Block b = (t) -> { return ExTest.maybeThrow(t);}; + } + + + private static class Ex extends Throwable { + public Ex(String s) { + } + } +} + +interface Block { + public void accept(T t); +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithTry/beforeLambdaExpr.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithTry/beforeLambdaExpr.java new file mode 100644 index 000000000000..2cb6adb41d38 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithTry/beforeLambdaExpr.java @@ -0,0 +1,20 @@ +// "Surround with try/catch" "false" +public class ExTest { + public static void maybeThrow(String data) throws Ex { + throw new Ex(data); + } + + { + Block b = (t) -> ExTest.maybeThrow(t); + } + + + private static class Ex extends Throwable { + public Ex(String s) { + } + } +} + +interface Block { + public void accept(T t); +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithTry/beforeMethodRef.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithTry/beforeMethodRef.java new file mode 100644 index 000000000000..80cc540bed71 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/surroundWithTry/beforeMethodRef.java @@ -0,0 +1,20 @@ +// "Surround with try/catch" "false" +public class ExTest { + public static void maybeThrow(String data) throws Ex { + throw new Ex(data); + } + + { + Block b = ExTest::maybeThrow(t); + } + + + private static class Ex extends Throwable { + public Ex(String s) { + } + } +} + +interface Block { + public void accept(T t); +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java index af820bdfb9a8..63d77f41b13e 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java @@ -150,6 +150,10 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testUnhandledExceptions() throws Exception { + doTest(); + } + public void testReturnValue() throws Exception { doTest(); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java index e1a3a9c22492..8ae41eb15e81 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java @@ -157,6 +157,10 @@ public class MethodRefHighlightingTest extends LightDaemonAnalyzerTestCase { doTest(false); } + public void testUnhandledExceptions() throws Exception { + doTest(); + } + private void doTest() throws Exception { doTest(false); }