mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-06 11:50:54 +07:00
lambda: skip some cyclic inference cases
This commit is contained in:
@@ -28,6 +28,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* User: anna
|
||||
@@ -76,8 +77,7 @@ public class LambdaUtil {
|
||||
|
||||
public static boolean isLambdaFullyInferred(PsiLambdaExpression expression, PsiType functionalInterfaceType) {
|
||||
if (expression.getParameterList().getParametersCount() > 0 || getFunctionalInterfaceReturnType(functionalInterfaceType) != PsiType.VOID) { //todo check that void lambdas without params check
|
||||
final Boolean accept = functionalInterfaceType.accept(new TypeParamsChecker(expression));
|
||||
return accept == null || !accept.booleanValue();
|
||||
return !dependsOnTypeParams(functionalInterfaceType, expression);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -262,7 +262,9 @@ public class LambdaUtil {
|
||||
}
|
||||
|
||||
public static boolean dependsOnTypeParams(PsiType type, PsiLambdaExpression expr) {
|
||||
final Boolean accept = type.accept(new TypeParamsChecker(expr));
|
||||
final TypeParamsChecker visitor = new TypeParamsChecker(expr);
|
||||
if (!visitor.startedInference()) return false;
|
||||
final Boolean accept = type.accept(visitor);
|
||||
return accept != null && accept.booleanValue();
|
||||
}
|
||||
|
||||
@@ -292,11 +294,130 @@ public class LambdaUtil {
|
||||
return independent[0];
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiType getFunctionalInterfaceType(PsiLambdaExpression expression, final boolean tryToSubstitute) {
|
||||
PsiElement parent = expression.getParent();
|
||||
while (parent instanceof PsiParenthesizedExpression) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
PsiType type = null;
|
||||
if (parent instanceof PsiTypeCastExpression) {
|
||||
type = ((PsiTypeCastExpression)parent).getType();
|
||||
}
|
||||
else if (parent instanceof PsiVariable) {
|
||||
type = ((PsiVariable)parent).getType();
|
||||
}
|
||||
else if (parent instanceof PsiAssignmentExpression) {
|
||||
final PsiExpression lExpression = ((PsiAssignmentExpression)parent).getLExpression();
|
||||
type = lExpression.getType();
|
||||
}
|
||||
else if (parent instanceof PsiExpressionList) {
|
||||
final PsiExpressionList expressionList = (PsiExpressionList)parent;
|
||||
int lambdaIdx = getLambdaIdx(expressionList, expression);
|
||||
if (lambdaIdx > -1) {
|
||||
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
|
||||
public PsiType compute() {
|
||||
return resolveResult.getSubstitutor().substitute(psiType);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (parent instanceof PsiReturnStatement) {
|
||||
final PsiMethod method = PsiTreeUtil.getParentOfType(parent, PsiMethod.class);
|
||||
if (method != null) {
|
||||
type = method.getReturnType();
|
||||
}
|
||||
}
|
||||
else if (parent instanceof PsiLambdaExpression) {
|
||||
final PsiType parentInterfaceType = ((PsiLambdaExpression)parent).getFunctionalInterfaceType();
|
||||
if (parentInterfaceType != null) {
|
||||
type = getFunctionalInterfaceReturnType(parentInterfaceType);
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public static PsiType getLambdaParameterType(PsiParameter param) {
|
||||
final PsiElement paramParent = param.getParent();
|
||||
if (paramParent instanceof PsiParameterList) {
|
||||
final int parameterIndex = ((PsiParameterList)paramParent).getParameterIndex(param);
|
||||
if (parameterIndex > -1) {
|
||||
final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(param, PsiLambdaExpression.class);
|
||||
PsiType type = getFunctionalInterfaceType(lambdaExpression, true);
|
||||
if (type == null) {
|
||||
type = getFunctionalInterfaceType(lambdaExpression, false);
|
||||
}
|
||||
final PsiClassType.ClassResolveResult resolveResult = type instanceof PsiClassType ? ((PsiClassType)type).resolveGenerics() : null;
|
||||
if (resolveResult != null) {
|
||||
final PsiMethod method = getFunctionalInterfaceMethod(type);
|
||||
if (method != null) {
|
||||
final PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
if (parameterIndex < parameters.length) {
|
||||
final PsiType psiType = resolveResult.getSubstitutor().substitute(parameters[parameterIndex].getType());
|
||||
if (!dependsOnTypeParams(psiType, lambdaExpression)) {
|
||||
if (psiType instanceof PsiWildcardType) {
|
||||
final PsiType bound = ((PsiWildcardType)psiType).getBound();
|
||||
if (bound != null) {
|
||||
return bound;
|
||||
}
|
||||
}
|
||||
return psiType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new PsiLambdaParameterType(param);
|
||||
}
|
||||
|
||||
private static class TypeParamsChecker extends PsiTypeVisitor<Boolean> {
|
||||
private final PsiLambdaExpression myExpression;
|
||||
private PsiMethod myMethod;
|
||||
protected final PsiClass myClass;
|
||||
|
||||
public TypeParamsChecker(PsiLambdaExpression expression) {
|
||||
myExpression = expression;
|
||||
myClass = PsiUtil.resolveGenericsClassInType(getFunctionalInterfaceType(expression, false)).getElement();
|
||||
PsiElement parent = expression.getParent();
|
||||
while (parent instanceof PsiParenthesizedExpression) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
if (parent instanceof PsiExpressionList) {
|
||||
final PsiElement gParent = parent.getParent();
|
||||
if (gParent instanceof PsiCallExpression) {
|
||||
myMethod = ((PsiCallExpression)gParent).resolveMethod();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean startedInference() {
|
||||
return myMethod != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -307,7 +428,11 @@ public class LambdaUtil {
|
||||
}
|
||||
final PsiClass resolve = classType.resolve();
|
||||
if (resolve instanceof PsiTypeParameter) {
|
||||
if (!PsiTreeUtil.isAncestor(((PsiTypeParameter)resolve).getOwner(), myExpression, false)) {
|
||||
final PsiTypeParameterListOwner owner = ((PsiTypeParameter)resolve).getOwner();
|
||||
if (owner == myMethod) {
|
||||
return true;
|
||||
}
|
||||
else if (owner == myClass) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import com.intellij.psi.impl.java.stubs.PsiParameterStub;
|
||||
import com.intellij.psi.impl.source.tree.ChildRole;
|
||||
import com.intellij.psi.impl.source.tree.CompositeElement;
|
||||
import com.intellij.psi.impl.source.tree.JavaSharedImplUtil;
|
||||
import com.intellij.psi.impl.source.tree.java.PsiLambdaExpressionImpl;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.SearchScope;
|
||||
import com.intellij.ui.RowIcon;
|
||||
@@ -122,7 +121,7 @@ public class PsiParameterImpl extends JavaStubPsiElement<PsiParameterStub> imple
|
||||
|
||||
final PsiTypeElement typeElement = getTypeElement();
|
||||
if (typeElement == null && isLambdaParameter()) {
|
||||
return PsiLambdaExpressionImpl.getLambdaParameterType(this);
|
||||
return LambdaUtil.getLambdaParameterType(this);
|
||||
}
|
||||
|
||||
return JavaSharedImplUtil.getType(typeElement, getNameIdentifier(), this);
|
||||
|
||||
@@ -878,7 +878,7 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
|
||||
if (argumentList != null && PsiUtil.getLanguageLevel(argumentList).isAtLeast(LanguageLevel.JDK_1_8)) {
|
||||
for (PsiExpression expression : argumentList.getExpressions()) {
|
||||
if (expression instanceof PsiLambdaExpression) {
|
||||
final PsiType functionalInterfaceType = PsiLambdaExpressionImpl.getFunctionalInterfaceType(((PsiLambdaExpression)expression), false);
|
||||
final PsiType functionalInterfaceType = LambdaUtil.getFunctionalInterfaceType((PsiLambdaExpression)expression, false);
|
||||
if (functionalInterfaceType == null || PsiUtil.resolveClassInType(functionalInterfaceType) == typeParameter){
|
||||
return getFailedInferenceConstraint(typeParameter);
|
||||
}
|
||||
|
||||
@@ -15,14 +15,11 @@
|
||||
*/
|
||||
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;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.util.MethodSignature;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -30,7 +27,6 @@ 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 {
|
||||
|
||||
@@ -89,7 +85,7 @@ public class PsiLambdaExpressionImpl extends ExpressionPsiElement implements Psi
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiType getFunctionalInterfaceType() {
|
||||
return getFunctionalInterfaceType(this, true);
|
||||
return LambdaUtil.getFunctionalInterfaceType(this, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -110,74 +106,6 @@ public class PsiLambdaExpressionImpl extends ExpressionPsiElement implements Psi
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiType getFunctionalInterfaceType(PsiLambdaExpression expression, final boolean tryToSubstitute) {
|
||||
PsiElement parent = expression.getParent();
|
||||
while (parent instanceof PsiParenthesizedExpression) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
PsiType type = null;
|
||||
if (parent instanceof PsiTypeCastExpression) {
|
||||
type = ((PsiTypeCastExpression)parent).getType();
|
||||
}
|
||||
else if (parent instanceof PsiVariable) {
|
||||
type = ((PsiVariable)parent).getType();
|
||||
}
|
||||
else if (parent instanceof PsiAssignmentExpression) {
|
||||
final PsiExpression lExpression = ((PsiAssignmentExpression)parent).getLExpression();
|
||||
type = lExpression.getType();
|
||||
}
|
||||
else if (parent instanceof PsiExpressionList) {
|
||||
final PsiExpressionList expressionList = (PsiExpressionList)parent;
|
||||
int lambdaIdx = LambdaUtil.getLambdaIdx(expressionList, expression);
|
||||
if (lambdaIdx > -1) {
|
||||
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
|
||||
public PsiType compute() {
|
||||
return resolveResult.getSubstitutor().substitute(psiType);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (parent instanceof PsiReturnStatement) {
|
||||
final PsiMethod method = PsiTreeUtil.getParentOfType(parent, PsiMethod.class);
|
||||
if (method != null) {
|
||||
type = method.getReturnType();
|
||||
}
|
||||
}
|
||||
else if (parent instanceof PsiLambdaExpression) {
|
||||
final PsiType parentInterfaceType = ((PsiLambdaExpression)parent).getFunctionalInterfaceType();
|
||||
if (parentInterfaceType != null) {
|
||||
type = LambdaUtil.getFunctionalInterfaceReturnType(parentInterfaceType);
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiType getType() {
|
||||
return new PsiLambdaExpressionType(this);
|
||||
@@ -205,38 +133,4 @@ public class PsiLambdaExpressionImpl extends ExpressionPsiElement implements Psi
|
||||
public String toString() {
|
||||
return "PsiLambdaExpression:" + getText();
|
||||
}
|
||||
|
||||
public static PsiType getLambdaParameterType(PsiParameter param) {
|
||||
final PsiElement paramParent = param.getParent();
|
||||
if (paramParent instanceof PsiParameterList) {
|
||||
final int parameterIndex = ((PsiParameterList)paramParent).getParameterIndex(param);
|
||||
if (parameterIndex > -1) {
|
||||
final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(param, PsiLambdaExpression.class);
|
||||
PsiType type = getFunctionalInterfaceType(lambdaExpression, true);
|
||||
if (type == null) {
|
||||
type = getFunctionalInterfaceType(lambdaExpression, false);
|
||||
}
|
||||
final PsiClassType.ClassResolveResult resolveResult = type instanceof PsiClassType ? ((PsiClassType)type).resolveGenerics() : null;
|
||||
if (resolveResult != null) {
|
||||
final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(type);
|
||||
if (method != null) {
|
||||
final PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
if (parameterIndex < parameters.length) {
|
||||
final PsiType psiType = resolveResult.getSubstitutor().substitute(parameters[parameterIndex].getType());
|
||||
if (!LambdaUtil.dependsOnTypeParams(psiType, lambdaExpression)) {
|
||||
if (psiType instanceof PsiWildcardType) {
|
||||
final PsiType bound = ((PsiWildcardType)psiType).getBound();
|
||||
if (bound != null) {
|
||||
return bound;
|
||||
}
|
||||
}
|
||||
return psiType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new PsiLambdaParameterType(param);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ class InferenceOnMethodCallSite {
|
||||
<Z> void m(In<Z> s) { }
|
||||
|
||||
{
|
||||
m(<error descr="Cyclic inference">( ) -> Collections.emptyList()</error>);
|
||||
m(() -> Collections.emptyList());
|
||||
m((In<String>)() -> Collections.emptyList(), () -> new ArrayList<String>());
|
||||
m(() ->Collections.<String>emptyList(), () -> new ArrayList<String>());
|
||||
m(() -> Collections.<String>emptyList());
|
||||
|
||||
@@ -7,7 +7,7 @@ class NoInferenceResult {
|
||||
<T> void m1(T t) { }
|
||||
|
||||
void test() {
|
||||
m((String s1) -> <error descr="Cyclic inference">(String s2) -> s1 + s2</error>);
|
||||
m(<error descr="Cyclic inference">(String s1) -> (String s2) -> s1 + s2</error>);
|
||||
|
||||
m((String s1) -> s1.length());
|
||||
m((String s1) -> s1);
|
||||
|
||||
@@ -57,7 +57,7 @@ class ReturnTypeCompatibility {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
<error descr="Cannot resolve method 'call(<lambda expression>)'">call</error>(i-> {return i;});
|
||||
call(<error descr="Cyclic inference">i-> {return i;}</error>);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user