lambda: return type void-compatibility checks extracted

This commit is contained in:
Anna Kozlova
2012-08-20 15:26:45 +04:00
parent ff7e11f084
commit a060e747ab
9 changed files with 125 additions and 19 deletions
@@ -248,7 +248,14 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
myHolder.add(HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, notFunctionalMessage));
} else {
if (!LambdaUtil.isLambdaFullyInferred(expression, functionalInterfaceType)) {
myHolder.add(HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, "Cyclic inference")); //todo[ann] append not inferred type params info
myHolder.add(HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression,
"Cyclic inference")); //todo[ann] append not inferred type params info
}
else {
final String incompatibleReturnTypesMessage = LambdaUtil.checkReturnTypeCompatible(expression, functionalInterfaceType);
if (incompatibleReturnTypesMessage != null) {
myHolder.add(HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, incompatibleReturnTypesMessage));
}
}
}
}
@@ -98,6 +98,31 @@ public class LambdaUtil {
if (signatures.isEmpty()) return "No target method found";
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) {
final PsiElement body = lambdaExpression.getBody();
if (body instanceof PsiCodeBlock) {
if (!lambdaExpression.getReturnExpressions().isEmpty()) return "Cannot return a value from method whose result type is void";
} else if (body instanceof PsiExpression) {
final PsiType type = ((PsiExpression)body).getType();
return "Incompatible return type " + (type == PsiType.NULL || type == null ? "<null>" : type.getPresentableText()) +" in lambda expression";
}
} else if (returnType != null) {
final List<PsiExpression> returnExpressions = lambdaExpression.getReturnExpressions();
for (PsiExpression expression : returnExpressions) {
final PsiType expressionType = expression.getType();
if (expressionType != null && !returnType.isAssignableFrom(expressionType)) {
return "Incompatible return type " + expressionType.getPresentableText() + " in lambda expression";
}
}
if (returnExpressions.isEmpty()) {
return "Incompatible return type void in lambda expression";
}
}
return null;
}
public static boolean isAcceptable(PsiLambdaExpression lambdaExpression, final PsiType leftType) {
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(leftType);
@@ -19,6 +19,7 @@ import com.intellij.openapi.util.*;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.PsiTreeUtil;
@@ -28,6 +29,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class PsiLambdaExpressionImpl extends ExpressionPsiElement implements PsiLambdaExpression {
@@ -100,16 +102,16 @@ public class PsiLambdaExpressionImpl extends ExpressionPsiElement implements Psi
final PsiExpressionList expressionList = (PsiExpressionList)parent;
int lambdaIdx = LambdaUtil.getLambdaIdx(expressionList, expression);
if (lambdaIdx > -1) {
final PsiElement gParent = expressionList.getParent();
if (gParent instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression contextCall = (PsiMethodCallExpression)gParent;
final JavaResolveResult resolveResult = contextCall.resolveMethodGenerics();
final PsiElement resolve = resolveResult.getElement();
if (resolve instanceof PsiMethod) {
final PsiParameter[] parameters = ((PsiMethod)resolve).getParameterList().getParameters();
if (lambdaIdx < parameters.length) {
type = parameters[lambdaIdx].getType();
if (tryToSubstitute) {
if (tryToSubstitute) {
final PsiElement gParent = expressionList.getParent();
if (gParent instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression contextCall = (PsiMethodCallExpression)gParent;
final JavaResolveResult resolveResult = contextCall.resolveMethodGenerics();
final PsiElement resolve = resolveResult.getElement();
if (resolve instanceof PsiMethod) {
final PsiParameter[] parameters = ((PsiMethod)resolve).getParameterList().getParameters();
if (lambdaIdx < parameters.length) {
type = parameters[lambdaIdx].getType();
final PsiType psiType = type;
type = PsiResolveHelper.ourGuard.doPreventingRecursion(expression, true, new Computable<PsiType>() {
@Override
@@ -120,6 +122,15 @@ public class PsiLambdaExpressionImpl extends ExpressionPsiElement implements Psi
}
}
}
} else {
final Map<PsiElement,PsiMethod> currentMethodCandidates = MethodCandidateInfo.CURRENT_CANDIDATE.get();
final PsiMethod method = currentMethodCandidates != null ? currentMethodCandidates.get(parent) : null;
if (method != null) {
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (lambdaIdx < parameters.length) {
type = parameters[lambdaIdx].getType();
}
}
}
}
}
@@ -12,6 +12,6 @@ class Ambiguity1 {
static <T> void m(I2<T> i2) {}
{
m<error descr="Ambiguous method call: both 'Ambiguity1.m(I1)' and 'Ambiguity1.m(I2<T>)' match">(()->{throw new AssertionError();})</error>;
m<error descr="Ambiguous method call: both 'Ambiguity1.m(I1)' and 'Ambiguity1.m(I2<Object>)' match">(()->{throw new AssertionError();})</error>;
}
}
@@ -0,0 +1,59 @@
class Test1 {
interface VoidReturnType {
void foo();
}
{
VoidReturnType aI = <error descr="Incompatible return type void in lambda expression">() -> System.out.println()</error>;
VoidReturnType aI1 = () -> {System.out.println();};
VoidReturnType aI2 = <error descr="Cannot return a value from method whose result type is void">() -> {return 1;}</error>;
VoidReturnType aI3 = <error descr="Incompatible return type int in lambda expression">() -> 1</error>;
VoidReturnType aI4 = () -> {return;};
}
}
class Test2 {
interface IntReturnType {
int foo();
}
{
IntReturnType aI = <error descr="Incompatible return type void in lambda expression">() -> System.out.println()</error>;
IntReturnType aI1 = <error descr="Incompatible return type void in lambda expression">() -> {System.out.println();}</error>;
IntReturnType aI2 = () -> {return 1;};
IntReturnType aI3 = () -> 1;
}
}
class Test3 {
interface XReturnType<X> {
X foo();
}
{
XReturnType<Object> aI = <error descr="Incompatible return type void in lambda expression">() -> System.out.println()</error>;
XReturnType<Object> aI1 = <error descr="Incompatible return type void in lambda expression">() -> {System.out.println();}</error>;
XReturnType<Object> aI2 = () -> {return 1;};
XReturnType<Object> aI3 = () -> 1;
XReturnType<Object> aI4 = <error descr="Incompatible return type void in lambda expression">() -> {}</error>;
}
}
class Test4 {
class Y<T>{}
interface YXReturnType<X> {
Y<X> foo();
}
{
YXReturnType<Object> aI = <error descr="Incompatible return type void in lambda expression">() -> System.out.println()</error>;
YXReturnType<Object> aI1 = <error descr="Incompatible return type void in lambda expression">() -> {System.out.println();}</error>;
YXReturnType<Object> aI2 = <error descr="Incompatible return type int in lambda expression">() -> {return 1;}</error>;
YXReturnType<Object> aI3 = <error descr="Incompatible return type int in lambda expression">() -> 1</error>;
YXReturnType<Object> aI4 = () -> new Y<Object>(){};
YXReturnType<Object> aIDiamond = () -> new Y<>();
}
}
@@ -68,5 +68,5 @@ class ReturnTypeChecks1 {
}
I<Integer, Integer> accepted = i -> { return i; };
<error descr="Incompatible types. Found: '<lambda expression>', required: 'ReturnTypeChecks1.I<java.lang.Double,java.lang.Integer>'">I<Double, Integer> rejected = i -> { return i; };</error>
I<Double, Integer> rejected = <error descr="Incompatible return type Double in lambda expression">i -> { return i; }</error>;
}
@@ -32,7 +32,7 @@ class Test1 {
I<Object> lO = x->x;
bar2("", lO);
<error descr="Incompatible types. Found: '<lambda expression>', required: 'Test1.I<java.lang.String>'">I<String> lS = x->x;</error>
I<String> lS = <error descr="Incompatible return type List<String> in lambda expression">x->x</error>;
bar2("", lS);
bar2("", x -> x);
@@ -66,9 +66,9 @@ class Test2 {
{
bar(<error descr="Cyclic inference">x -> x</error>);
bar1(<error descr="Cyclic inference">x -> x</error>);
bar2<error descr="'bar2(java.lang.Integer, Test2.I<java.lang.Integer>)' in 'Test2' cannot be applied to '(int, <lambda expression>)'">(1, x -> x)</error>;
bar2<error descr="'bar2(java.lang.String, Test2.I<java.lang.String>)' in 'Test2' cannot be applied to '(java.lang.String, <lambda expression>)'">("", x -> x)</error>;
bar3<error descr="'bar3(Test2.I<java.lang.String>, java.lang.String)' in 'Test2' cannot be applied to '(<lambda expression>, java.lang.String)'">(x -> x, "")</error>;
bar2(1, <error descr="Incompatible return type List<Integer> in lambda expression">x -> x</error>);
bar2("", <error descr="Incompatible return type List<String> in lambda expression">x -> x</error>);
bar3(<error descr="Incompatible return type List<String> in lambda expression">x -> x</error>, "");
}
}
@@ -19,7 +19,7 @@ class Test5 {
static <T> void bar(I<T> i){}
{
bar<error descr="'bar(Test5.I<T>)' in 'Test5' cannot be applied to '(<lambda expression>)'">(() -> null)</error>;
bar(<error descr="Incompatible return type <null> in lambda expression">() -> null</error>);
}
}
class Test6 {
@@ -30,7 +30,7 @@ class Test6 {
static <T> void bar(I<T> i){}
{
bar<error descr="'bar(Test6.I<java.lang.Object>)' in 'Test6' cannot be applied to '(<lambda expression>)'">(() -> null)</error>;
bar(<error descr="Incompatible return type <null> in lambda expression">() -> null</error>);
bar(() -> {});
}
}
@@ -51,6 +51,10 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testTypeArgsConsistencyWithoutParams() throws Exception {
doTest();
}
public void testIncompatibleReturnTypes() throws Exception {
doTest();
}
public void testWildcardBounds() throws Exception {