lambda return type checks: consider void-compatible/value-compatible block

This commit is contained in:
Anna Kozlova
2012-08-20 16:59:18 +04:00
parent 9cdf03c1fd
commit b6a278b60f
5 changed files with 56 additions and 55 deletions
@@ -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));
}
@@ -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 ? "<null>" : type.getPresentableText()) +" in lambda expression";
}
}
} else if (returnType != null) {
} else if (functionalInterfaceReturnType != null) {
final List<PsiExpression> 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<PsiExpression> 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++) {
@@ -42,7 +42,10 @@ public interface PsiLambdaExpression extends PsiExpression {
PsiElement getBody();
List<PsiExpression> getReturnExpressions();
List<PsiReturnStatement> getReturnStatements();
@Nullable
PsiType getFunctionalInterfaceType();
boolean isVoidCompatible();
}
@@ -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<PsiExpression> getReturnExpressions() {
public List<PsiReturnStatement> 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<PsiExpression> result = new ArrayList<PsiExpression>();
final List<PsiReturnStatement> result = new ArrayList<PsiReturnStatement>();
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<PsiExpression> 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<PsiExpression> result = new ArrayList<PsiExpression>();
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();
@@ -13,5 +13,10 @@ class Ambiguity1 {
{
m<error descr="Ambiguous method call: both 'Ambiguity1.m(I1)' and 'Ambiguity1.m(I2<Object>)' match">(()->{throw new AssertionError();})</error>;
m(() -> {});
m(() -> {
if (false) return;
throw new RuntimeException();
});
}
}