mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
lambda return type checks: consider void-compatible/value-compatible block
This commit is contained in:
+1
-1
@@ -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();
|
||||
}
|
||||
|
||||
+39
-10
@@ -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();
|
||||
|
||||
+5
@@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user