mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
new inference: isAcceptable for lambda
This commit is contained in:
@@ -20,7 +20,6 @@ import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.infos.MethodCandidateInfo;
|
||||
import com.intellij.psi.util.*;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -128,89 +127,6 @@ public class LambdaUtil {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean checkRawAcceptable(PsiLambdaExpression expression, PsiType functionalInterfaceType) {
|
||||
PsiElement parent = expression.getParent();
|
||||
while (parent instanceof PsiParenthesizedExpression) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
if (parent instanceof PsiExpressionList) {
|
||||
final PsiElement gParent = parent.getParent();
|
||||
if (gParent instanceof PsiMethodCallExpression) {
|
||||
final PsiExpression qualifierExpression = ((PsiMethodCallExpression)gParent).getMethodExpression().getQualifierExpression();
|
||||
final PsiType type = qualifierExpression != null ? qualifierExpression.getType() : null;
|
||||
if (type instanceof PsiClassType && ((PsiClassType)type).isRaw()) {
|
||||
return true;
|
||||
}
|
||||
final PsiMethod method = ((PsiMethodCallExpression)gParent).resolveMethod();
|
||||
if (method != null) {
|
||||
int lambdaIdx = getLambdaIdx((PsiExpressionList)parent, expression);
|
||||
final PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
final PsiType normalizedType = getNormalizedType(parameters[adjustLambdaIdx(lambdaIdx, method, parameters)]);
|
||||
if (normalizedType instanceof PsiClassType && ((PsiClassType)normalizedType).isRaw()) return true;
|
||||
}
|
||||
}
|
||||
if (functionalInterfaceType instanceof PsiClassType && ((PsiClassType)functionalInterfaceType).isRaw()){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isAcceptable(PsiLambdaExpression lambdaExpression, final PsiType leftType, boolean checkReturnType) {
|
||||
if (leftType instanceof PsiIntersectionType) {
|
||||
for (PsiType conjunctType : ((PsiIntersectionType)leftType).getConjuncts()) {
|
||||
if (isAcceptable(lambdaExpression, conjunctType, checkReturnType)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(GenericsUtil.eliminateWildcards(leftType));
|
||||
final PsiClass psiClass = resolveResult.getElement();
|
||||
if (psiClass instanceof PsiAnonymousClass) {
|
||||
return isAcceptable(lambdaExpression, ((PsiAnonymousClass)psiClass).getBaseClassType(), checkReturnType);
|
||||
}
|
||||
final MethodSignature methodSignature = getFunction(psiClass);
|
||||
if (methodSignature == null) return false;
|
||||
final PsiParameter[] lambdaParameters = lambdaExpression.getParameterList().getParameters();
|
||||
final PsiType[] parameterTypes = methodSignature.getParameterTypes();
|
||||
if (lambdaParameters.length != parameterTypes.length) return false;
|
||||
for (int lambdaParamIdx = 0, length = lambdaParameters.length; lambdaParamIdx < length; lambdaParamIdx++) {
|
||||
PsiParameter parameter = lambdaParameters[lambdaParamIdx];
|
||||
final PsiTypeElement typeElement = parameter.getTypeElement();
|
||||
if (typeElement != null) {
|
||||
final PsiType lambdaFormalType = typeElement.getType();
|
||||
final PsiType methodParameterType = parameterTypes[lambdaParamIdx];
|
||||
if (lambdaFormalType instanceof PsiPrimitiveType) {
|
||||
if (methodParameterType instanceof PsiPrimitiveType) return methodParameterType.equals(lambdaFormalType);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TypeConversionUtil.erasure(lambdaFormalType)
|
||||
.isAssignableFrom(TypeConversionUtil.erasure(GenericsUtil.eliminateWildcards(
|
||||
resolveResult.getSubstitutor().substitute(methodSignature.getSubstitutor().substitute(methodParameterType)))))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (checkReturnType) {
|
||||
final String uniqueVarName =
|
||||
JavaCodeStyleManager.getInstance(lambdaExpression.getProject()).suggestUniqueVariableName("l", lambdaExpression, true);
|
||||
String canonicalText = leftType.getCanonicalText();
|
||||
if (leftType instanceof PsiEllipsisType) {
|
||||
canonicalText = ((PsiEllipsisType)leftType).toArrayType().getCanonicalText();
|
||||
}
|
||||
final PsiStatement assignmentFromText = JavaPsiFacade.getElementFactory(lambdaExpression.getProject())
|
||||
.createStatementFromText(canonicalText + " " + uniqueVarName + " = " + lambdaExpression.getText(), lambdaExpression);
|
||||
final PsiLocalVariable localVariable = (PsiLocalVariable)((PsiDeclarationStatement)assignmentFromText).getDeclaredElements()[0];
|
||||
LOG.assertTrue(psiClass != null);
|
||||
PsiType methodReturnType = getReturnType(psiClass, methodSignature);
|
||||
if (methodReturnType != null) {
|
||||
methodReturnType = resolveResult.getSubstitutor().substitute(methodSignature.getSubstitutor().substitute(methodReturnType));
|
||||
return LambdaHighlightingUtil.checkReturnTypeCompatible((PsiLambdaExpression)localVariable.getInitializer(), methodReturnType) == null;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static MethodSignature getFunction(PsiClass psiClass) {
|
||||
if (psiClass == null) return null;
|
||||
|
||||
@@ -53,4 +53,6 @@ public interface PsiLambdaExpression extends PsiExpression {
|
||||
* @return true when lambda declares parameter types explicitly
|
||||
*/
|
||||
boolean hasFormalParameterTypes();
|
||||
|
||||
boolean isAcceptable(PsiType leftType, boolean checkReturnType);
|
||||
}
|
||||
|
||||
@@ -716,7 +716,7 @@ public class TypeConversionUtil {
|
||||
final PsiType lType = lLambdaExpression.getFunctionalInterfaceType();
|
||||
return Comparing.equal(rType, lType);
|
||||
}
|
||||
return !(left instanceof PsiArrayType) && LambdaUtil.isAcceptable(rLambdaExpression, left, false);
|
||||
return !(left instanceof PsiArrayType) && rLambdaExpression.isAcceptable(left, false);
|
||||
}
|
||||
|
||||
if (left instanceof PsiIntersectionType) {
|
||||
|
||||
+4
@@ -123,6 +123,10 @@ public class FunctionalInterfaceParameterizationUtil {
|
||||
return parameterization;
|
||||
}
|
||||
|
||||
if (!parameterization.isAssignableFrom(psiClassType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getNonWildcardParameterization(parameterization);
|
||||
}
|
||||
return null;
|
||||
|
||||
+1
@@ -747,6 +747,7 @@ public class InferenceSession {
|
||||
LOG.assertTrue(var.getInstantiation() == PsiType.NULL);
|
||||
final PsiTypeParameter typeParameter = var.getParameter();
|
||||
final PsiType eqBound = getEqualsBound(var, substitutor);
|
||||
if (eqBound != PsiType.NULL && eqBound instanceof PsiPrimitiveType) continue;
|
||||
final PsiType lub = eqBound != PsiType.NULL && (myErased || eqBound != null) ? eqBound : getLowerBound(var, substitutor);
|
||||
if (lub != PsiType.NULL) {
|
||||
substitutor = substitutor.put(typeParameter, lub);
|
||||
|
||||
+59
@@ -17,6 +17,7 @@ package com.intellij.psi.impl.source.tree.java;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.controlFlow.*;
|
||||
import com.intellij.psi.impl.PsiImplUtil;
|
||||
import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil;
|
||||
@@ -25,6 +26,7 @@ import com.intellij.psi.impl.source.tree.JavaElementType;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.containers.IntArrayList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -142,4 +144,61 @@ public class PsiLambdaExpressionImpl extends ExpressionPsiElement implements Psi
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAcceptable(PsiType leftType, boolean checkReturnType) {
|
||||
if (leftType instanceof PsiIntersectionType) {
|
||||
for (PsiType conjunctType : ((PsiIntersectionType)leftType).getConjuncts()) {
|
||||
if (isAcceptable(conjunctType, checkReturnType)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
leftType = FunctionalInterfaceParameterizationUtil.getGroundTargetType(leftType, this);
|
||||
|
||||
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(leftType);
|
||||
final PsiClass psiClass = resolveResult.getElement();
|
||||
if (psiClass instanceof PsiAnonymousClass) {
|
||||
return isAcceptable(((PsiAnonymousClass)psiClass).getBaseClassType(), checkReturnType);
|
||||
}
|
||||
|
||||
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
|
||||
|
||||
if (interfaceMethod == null) return false;
|
||||
|
||||
final PsiSubstitutor substitutor = LambdaUtil.getSubstitutor(interfaceMethod, resolveResult);
|
||||
|
||||
assert leftType != null;
|
||||
final PsiParameter[] lambdaParameters = getParameterList().getParameters();
|
||||
final PsiType[] parameterTypes = interfaceMethod.getSignature(substitutor).getParameterTypes();
|
||||
if (lambdaParameters.length != parameterTypes.length) return false;
|
||||
|
||||
for (int lambdaParamIdx = 0, length = lambdaParameters.length; lambdaParamIdx < length; lambdaParamIdx++) {
|
||||
PsiParameter parameter = lambdaParameters[lambdaParamIdx];
|
||||
final PsiTypeElement typeElement = parameter.getTypeElement();
|
||||
if (typeElement != null) {
|
||||
final PsiType lambdaFormalType = typeElement.getType();
|
||||
final PsiType methodParameterType = parameterTypes[lambdaParamIdx];
|
||||
if (!lambdaFormalType.equals(methodParameterType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (checkReturnType) {
|
||||
final String uniqueVarName = JavaCodeStyleManager.getInstance(getProject()).suggestUniqueVariableName("l", this, true);
|
||||
String canonicalText = leftType.getCanonicalText();
|
||||
if (leftType instanceof PsiEllipsisType) {
|
||||
canonicalText = ((PsiEllipsisType)leftType).toArrayType().getCanonicalText();
|
||||
}
|
||||
final PsiStatement assignmentFromText = JavaPsiFacade.getElementFactory(getProject())
|
||||
.createStatementFromText(canonicalText + " " + uniqueVarName + " = " + getText(), this);
|
||||
final PsiLocalVariable localVariable = (PsiLocalVariable)((PsiDeclarationStatement)assignmentFromText).getDeclaredElements()[0];
|
||||
PsiType methodReturnType = interfaceMethod.getReturnType();
|
||||
if (methodReturnType != null) {
|
||||
return LambdaHighlightingUtil.checkReturnTypeCompatible((PsiLambdaExpression)localVariable.getInitializer(),
|
||||
substitutor.substitute(methodReturnType)) == null;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -121,8 +121,7 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
|
||||
if (methodParameters.length == 0) continue;
|
||||
final PsiParameter param = i < methodParameters.length ? methodParameters[i] : methodParameters[methodParameters.length - 1];
|
||||
final PsiType paramType = param.getType();
|
||||
if (!LambdaUtil.isAcceptable(lambdaExpression, ((MethodCandidateInfo)conflict).getSubstitutor(false).substitute(paramType),
|
||||
lambdaExpression.hasFormalParameterTypes())) {
|
||||
if (!lambdaExpression.isAcceptable(((MethodCandidateInfo)conflict).getSubstitutor(false).substitute(paramType), lambdaExpression.hasFormalParameterTypes())) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ class Test {
|
||||
void foo(I<? extends String, ? extends List<? extends String>> fip) { }
|
||||
|
||||
void test() {
|
||||
foo<error descr="'foo(Test.I<? extends java.lang.String,? extends java.util.List<? extends java.lang.String>>)' in 'Test' cannot be applied to '(<lambda expression>)'">((ArrayList<? extends String> p) -> p.get(0))</error>;
|
||||
foo(<error descr="Cannot infer functional interface type">(ArrayList<? extends String> p) -> p.get(0)</error>);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -32,6 +32,6 @@ class ReturnTypeCompatibility {
|
||||
call((String i)->{ return i;});
|
||||
call(i->{ return i;});
|
||||
call(i->"");
|
||||
call<error descr="'call(ReturnTypeCompatibility.I1<int>)' in 'ReturnTypeCompatibility' cannot be applied to '(<lambda expression>)'">((int i)->{ return i;})</error>;
|
||||
call(<error descr="Incompatible return type int in lambda expression">(int i)->{ return i;}</error>);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user