mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
lambda: return type void-compatibility checks extracted
This commit is contained in:
+8
-1
@@ -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);
|
||||
|
||||
+21
-10
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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>;
|
||||
}
|
||||
}
|
||||
|
||||
+59
@@ -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<>();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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>;
|
||||
}
|
||||
|
||||
+4
-4
@@ -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>, "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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(() -> {});
|
||||
}
|
||||
}
|
||||
+4
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user