mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-07 05:09:37 +07:00
lambda: return type checks initial
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.intellij.psi;
|
package com.intellij.psi;
|
||||||
|
|
||||||
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
import com.intellij.psi.util.*;
|
import com.intellij.psi.util.*;
|
||||||
import com.intellij.util.ArrayUtil;
|
import com.intellij.util.ArrayUtil;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -29,6 +30,8 @@ import java.util.List;
|
|||||||
* Date: 7/17/12
|
* Date: 7/17/12
|
||||||
*/
|
*/
|
||||||
public class LambdaUtil {
|
public class LambdaUtil {
|
||||||
|
private static final Logger LOG = Logger.getInstance("#" + LambdaUtil.class.getName());
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static String checkInterfaceFunctional(@NotNull PsiClass psiClass) {
|
public static String checkInterfaceFunctional(@NotNull PsiClass psiClass) {
|
||||||
final List<MethodSignature> signatures = findFunctionCandidates(psiClass);
|
final List<MethodSignature> signatures = findFunctionCandidates(psiClass);
|
||||||
@@ -140,6 +143,10 @@ public class LambdaUtil {
|
|||||||
else if (parent instanceof PsiVariable) {
|
else if (parent instanceof PsiVariable) {
|
||||||
type = ((PsiVariable)parent).getType();
|
type = ((PsiVariable)parent).getType();
|
||||||
}
|
}
|
||||||
|
else if (parent instanceof PsiAssignmentExpression) {
|
||||||
|
final PsiExpression lExpression = ((PsiAssignmentExpression)parent).getLExpression();
|
||||||
|
type = lExpression.getType();
|
||||||
|
}
|
||||||
else if (parent instanceof PsiExpressionList) {
|
else if (parent instanceof PsiExpressionList) {
|
||||||
final PsiExpressionList expressionList = (PsiExpressionList)parent;
|
final PsiExpressionList expressionList = (PsiExpressionList)parent;
|
||||||
final int lambdaIdx = ArrayUtil.find(expressionList.getExpressions(), lambdaExpression);
|
final int lambdaIdx = ArrayUtil.find(expressionList.getExpressions(), lambdaExpression);
|
||||||
@@ -173,7 +180,8 @@ public class LambdaUtil {
|
|||||||
|
|
||||||
public static boolean isAcceptable(PsiLambdaExpression lambdaExpression, final PsiType leftType) {
|
public static boolean isAcceptable(PsiLambdaExpression lambdaExpression, final PsiType leftType) {
|
||||||
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(leftType);
|
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(leftType);
|
||||||
final MethodSignature methodSignature = getFunction(resolveResult.getElement());
|
final PsiClass psiClass = resolveResult.getElement();
|
||||||
|
final MethodSignature methodSignature = getFunction(psiClass);
|
||||||
if (methodSignature == null) return false;
|
if (methodSignature == null) return false;
|
||||||
final PsiParameter[] lambdaParameters = lambdaExpression.getParameterList().getParameters();
|
final PsiParameter[] lambdaParameters = lambdaExpression.getParameterList().getParameters();
|
||||||
final PsiType[] parameterTypes = methodSignature.getParameterTypes();
|
final PsiType[] parameterTypes = methodSignature.getParameterTypes();
|
||||||
@@ -182,11 +190,57 @@ public class LambdaUtil {
|
|||||||
PsiParameter parameter = lambdaParameters[lambdaParamIdx];
|
PsiParameter parameter = lambdaParameters[lambdaParamIdx];
|
||||||
final PsiTypeElement typeElement = parameter.getTypeElement();
|
final PsiTypeElement typeElement = parameter.getTypeElement();
|
||||||
if (typeElement != null) {
|
if (typeElement != null) {
|
||||||
if (!typeElement.getType().equals(resolveResult.getSubstitutor().substitute(parameterTypes[lambdaParamIdx]))) {
|
final PsiType lambdaFormalType = typeElement.getType();
|
||||||
|
final PsiType methodParameterType = parameterTypes[lambdaParamIdx];
|
||||||
|
if (lambdaFormalType instanceof PsiPrimitiveType){
|
||||||
|
if (methodParameterType instanceof PsiPrimitiveType) return methodParameterType.isAssignableFrom(lambdaFormalType);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resolveResult.getSubstitutor().substitute(methodParameterType).isAssignableFrom(lambdaFormalType)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PsiMethod method = null;
|
||||||
|
LOG.assertTrue(psiClass != null);
|
||||||
|
final PsiMethod[] methodsByName = psiClass.findMethodsByName(methodSignature.getName(), true);
|
||||||
|
for (PsiMethod psiMethod : methodsByName) {
|
||||||
|
if (MethodSignatureUtil.areSignaturesEqual(getMethodSignature(psiMethod, psiClass, psiMethod.getContainingClass()), methodSignature)) {
|
||||||
|
method = psiMethod;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG.assertTrue(method != null);
|
||||||
|
PsiType methodReturnType = method.getReturnType();
|
||||||
|
if (methodReturnType != null && methodReturnType != PsiType.VOID) {
|
||||||
|
methodReturnType = resolveResult.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,4 +70,8 @@ public class PsiLambdaParameterType extends PsiType {
|
|||||||
public PsiType[] getSuperTypes() {
|
public PsiType[] getSuperTypes() {
|
||||||
return PsiType.EMPTY_ARRAY;
|
return PsiType.EMPTY_ARRAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PsiParameter getParameter() {
|
||||||
|
return myParameter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
class ReturnTypeIncompatibility {
|
||||||
|
|
||||||
|
interface I1<T extends Number> {
|
||||||
|
T m(Integer x);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface I2<L extends String> {
|
||||||
|
L m(Integer x);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface I3<K> {
|
||||||
|
void m(Integer x);
|
||||||
|
}
|
||||||
|
|
||||||
|
static <P extends Number> void call(I1<P> i1) {
|
||||||
|
i1.m(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static <P extends String> void call(I2<P> i2) {
|
||||||
|
i2.m(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static <Q> void call(I3<Q> i3) {
|
||||||
|
i3.m(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
call(i-> {return i;});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ReturnTypeCompatibility {
|
||||||
|
|
||||||
|
interface I1<T extends Number> {
|
||||||
|
T m(T x);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface I2<L extends String> {
|
||||||
|
L m(L x);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface I3<K> {
|
||||||
|
void m(K x);
|
||||||
|
}
|
||||||
|
|
||||||
|
static <P extends Number> void call(I1<P> i1) {
|
||||||
|
i1.m(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static <P extends String> void call(I2<P> i2) {
|
||||||
|
i2.m(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static <Q> void call(I3<Q> i3) {
|
||||||
|
i3.m(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
<error descr="Cannot resolve method 'call(<lambda expression>)'">call</error>(i-> {return i;});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReturnTypeChecks1 {
|
||||||
|
|
||||||
|
interface I<K extends Number, V extends Number> {
|
||||||
|
V m(K k);
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
|
}
|
||||||
@@ -16,4 +16,20 @@ class Foo {
|
|||||||
bar((int i) -> {System.out.println(i);});
|
bar((int i) -> {System.out.println(i);});
|
||||||
}
|
}
|
||||||
void bar(I i){}
|
void bar(I i){}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReturnTypeCompatibility {
|
||||||
|
|
||||||
|
interface I1<L> {
|
||||||
|
L m(L x);
|
||||||
|
}
|
||||||
|
|
||||||
|
static <P> void call(I1<P> i2) {
|
||||||
|
i2.m(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
call((String i)->{ return i;});
|
||||||
|
call<error descr="'call(ReturnTypeCompatibility.I1<java.lang.Object>)' in 'ReturnTypeCompatibility' cannot be applied to '(<lambda expression>)'">((int i)->{ return i;})</error>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import java.lang.Integer;
|
||||||
|
|
||||||
interface I {
|
interface I {
|
||||||
void m(int i);
|
void m(int i);
|
||||||
}
|
}
|
||||||
@@ -18,6 +20,14 @@ class Foo {
|
|||||||
<error descr="Incompatible types. Found: 'java.lang.String', required: 'int'">int i = ab;</error>
|
<error descr="Incompatible types. Found: 'java.lang.String', required: 'int'">int i = ab;</error>
|
||||||
};
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
A<String> a1;
|
||||||
|
a1 = (ab)->{
|
||||||
|
String s = ab;
|
||||||
|
<error descr="Incompatible types. Found: 'java.lang.String', required: 'int'">int i = ab;</error>
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
A<Integer> bazz() {
|
A<Integer> bazz() {
|
||||||
bar((o) -> {
|
bar((o) -> {
|
||||||
String s = o;
|
String s = o;
|
||||||
@@ -33,3 +43,18 @@ class Foo {
|
|||||||
void bar(A<String> a){}
|
void bar(A<String> a){}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CastInference {
|
||||||
|
public interface I1<X> {
|
||||||
|
X m();
|
||||||
|
}
|
||||||
|
public interface I2<X> {
|
||||||
|
X m();
|
||||||
|
}
|
||||||
|
public static <X> void foo(I1<X> s) {}
|
||||||
|
public static <X> void foo(I2<X> s) {}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
foo((I1<Integer>)() -> 42);
|
||||||
|
I1<Integer> i1 = (I1<Integer>)() -> 42;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,6 +37,10 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
|
|||||||
doTest();
|
doTest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testReturnTypeCompatibility() throws Exception {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
private void doTest() throws Exception {
|
private void doTest() throws Exception {
|
||||||
doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false);
|
doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user