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 3de2e4db581a..b49a4994b796 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 @@ -252,7 +252,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh "Cyclic inference")); //todo[ann] append not inferred type params info } else { - final String incompatibleReturnTypesMessage = LambdaUtil.checkReturnTypeCompatible(expression, functionalInterfaceType); + final String incompatibleReturnTypesMessage = LambdaUtil.checkReturnTypeCompatible(expression, LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType)); if (incompatibleReturnTypesMessage != null) { myHolder.add(HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, incompatibleReturnTypesMessage)); } diff --git a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java index 683dd21f80fb..c408b62f9d5b 100644 --- a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java @@ -99,9 +99,8 @@ public class LambdaUtil { return signatures.size() == 1 ? null : "Multiple non-overriding abstract methods found"; } - public static String checkReturnTypeCompatible(PsiLambdaExpression lambdaExpression, PsiType functionalInterfaceType) { - final PsiType returnType = getFunctionalInterfaceReturnType(functionalInterfaceType); - if (returnType == PsiType.VOID) { + public static String checkReturnTypeCompatible(PsiLambdaExpression lambdaExpression, PsiType functionalInterfaceReturnType) { + if (functionalInterfaceReturnType == PsiType.VOID) { final PsiElement body = lambdaExpression.getBody(); if (body instanceof PsiCodeBlock) { if (!lambdaExpression.getReturnExpressions().isEmpty()) return "Unexpected return value"; @@ -111,16 +110,16 @@ public class LambdaUtil { return "Incompatible return type " + (type == PsiType.NULL || type == null ? "" : type.getPresentableText()) +" in lambda expression"; } } - } else if (returnType != null) { + } else if (functionalInterfaceReturnType != null) { final List returnExpressions = lambdaExpression.getReturnExpressions(); for (PsiExpression expression : returnExpressions) { final PsiType expressionType = expression.getType(); - if (expressionType != null && !returnType.isAssignableFrom(expressionType)) { + if (expressionType != null && !functionalInterfaceReturnType.isAssignableFrom(expressionType)) { return "Incompatible return type " + expressionType.getPresentableText() + " in lambda expression"; } } - if (returnExpressions.isEmpty()) { - return "Missing return value"; + if (lambdaExpression.getReturnStatements().size() > returnExpressions.size() || returnExpressions.isEmpty() && !lambdaExpression.isVoidCompatible()) { + return "Missing return value"; } } return null; @@ -154,29 +153,8 @@ public class LambdaUtil { LOG.assertTrue(psiClass != null); PsiType methodReturnType = getReturnType(psiClass, methodSignature); if (methodReturnType != null) { - if (methodReturnType != PsiType.VOID) { - methodReturnType = resolveResult.getSubstitutor().substitute(methodSignature.getSubstitutor().substitute(methodReturnType)); - final PsiElement body = lambdaExpression.getBody(); - if (body instanceof PsiCodeBlock) { - final PsiCodeBlock block = (PsiCodeBlock)body; - for (PsiStatement statement : block.getStatements()) { - if (statement instanceof PsiReturnStatement) { - final PsiExpression returnValue = ((PsiReturnStatement)statement).getReturnValue(); - if (returnValue != null) { - if (!checkReturnTypeAssignability(returnValue.getType(), parameterTypes, lambdaExpression, methodReturnType)) return false; - } - } - } - } - else if (body instanceof PsiExpression) { - return checkReturnTypeAssignability(((PsiExpression)body).getType(), parameterTypes, lambdaExpression, methodReturnType); - } - } else { - final List returnExpressions = lambdaExpression.getReturnExpressions(); - for (PsiExpression returnValue : returnExpressions) { - if (returnValue.getType() != PsiType.VOID) return false; - } - } + methodReturnType = resolveResult.getSubstitutor().substitute(methodSignature.getSubstitutor().substitute(methodReturnType)); + return checkReturnTypeCompatible(lambdaExpression, methodReturnType) == null; } return true; } @@ -271,20 +249,6 @@ public class LambdaUtil { return null; } - private static boolean checkReturnTypeAssignability(PsiType lambdaReturnType, - PsiType[] parameterTypes, - PsiLambdaExpression lambdaExpression, - PsiType methodReturnType) { - if (lambdaReturnType instanceof PsiLambdaParameterType) { - final PsiParameter parameter = ((PsiLambdaParameterType)lambdaReturnType).getParameter(); - final int parameterIndex = lambdaExpression.getParameterList().getParameterIndex(parameter); - if (parameterIndex > -1) { - lambdaReturnType = parameterTypes[parameterIndex]; - } - } - return lambdaReturnType != null && methodReturnType.isAssignableFrom(lambdaReturnType); - } - public static int getLambdaIdx(PsiExpressionList expressionList, final PsiLambdaExpression element) { PsiExpression[] expressions = expressionList.getExpressions(); for (int i = 0; i < expressions.length; i++) { diff --git a/java/java-psi-api/src/com/intellij/psi/PsiLambdaExpression.java b/java/java-psi-api/src/com/intellij/psi/PsiLambdaExpression.java index e02aca8b3c3d..c4682c32291a 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiLambdaExpression.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiLambdaExpression.java @@ -42,7 +42,10 @@ public interface PsiLambdaExpression extends PsiExpression { PsiElement getBody(); List getReturnExpressions(); + List getReturnStatements(); @Nullable PsiType getFunctionalInterfaceType(); + + boolean isVoidCompatible(); } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiLambdaExpressionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiLambdaExpressionImpl.java index 9e1b0718c26f..bcbcf0a7fe9c 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiLambdaExpressionImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiLambdaExpressionImpl.java @@ -17,6 +17,7 @@ package com.intellij.psi.impl.source.tree.java; import com.intellij.openapi.util.*; import com.intellij.psi.*; +import com.intellij.psi.controlFlow.*; import com.intellij.psi.impl.PsiImplUtil; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.infos.MethodCandidateInfo; @@ -50,21 +51,14 @@ public class PsiLambdaExpressionImpl extends ExpressionPsiElement implements Psi } @Override - public List getReturnExpressions() { + public List getReturnStatements() { final PsiElement body = getBody(); - if (body instanceof PsiExpression) { - //if (((PsiExpression)body).getType() != PsiType.VOID) return Collections.emptyList(); - return Collections.singletonList((PsiExpression)body); - } - final List result = new ArrayList(); + final List result = new ArrayList(); if (body != null) { body.accept(new JavaRecursiveElementVisitor() { @Override public void visitReturnStatement(PsiReturnStatement statement) { - final PsiExpression returnValue = statement.getReturnValue(); - if (returnValue != null) { - result.add(returnValue); - } + result.add(statement); } @Override @@ -75,12 +69,47 @@ public class PsiLambdaExpressionImpl extends ExpressionPsiElement implements Psi return result; } + @Override + public List getReturnExpressions() { + final PsiElement body = getBody(); + if (body instanceof PsiExpression) { + //if (((PsiExpression)body).getType() != PsiType.VOID) return Collections.emptyList(); + return Collections.singletonList((PsiExpression)body); + } + final List result = new ArrayList(); + for (PsiReturnStatement returnStatement : getReturnStatements()) { + final PsiExpression returnValue = returnStatement.getReturnValue(); + if (returnValue != null) { + result.add(returnValue); + } + } + return result; + } + @Nullable @Override public PsiType getFunctionalInterfaceType() { return getFunctionalInterfaceType(this, true); } + @Override + public boolean isVoidCompatible() { + final PsiElement body = getBody(); + if (body != null) { + try { + ControlFlow controlFlow = ControlFlowFactory.getInstance(getProject()).getControlFlow(body, LocalsOrMyInstanceFieldsControlFlowPolicy + .getInstance()); + int startOffset = controlFlow.getStartOffset(body); + int endOffset = controlFlow.getEndOffset(body); + return startOffset != -1 && endOffset != -1 && !ControlFlowUtil.canCompleteNormally(controlFlow, startOffset, endOffset); + } + catch (AnalysisCanceledException e) { + return true; + } + } + return true; + } + @Nullable public static PsiType getFunctionalInterfaceType(PsiLambdaExpression expression, final boolean tryToSubstitute) { PsiElement parent = expression.getParent(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/Ambiguity1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/Ambiguity1.java index 87f4b168fede..e66630146e6f 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/Ambiguity1.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/Ambiguity1.java @@ -13,5 +13,10 @@ class Ambiguity1 { { m(()->{throw new AssertionError();}); + m(() -> {}); + m(() -> { + if (false) return; + throw new RuntimeException(); + }); } }