lambda: skip some cyclic inference cases

This commit is contained in:
Anna Kozlova
2012-08-21 17:04:40 +04:00
parent 03e883689f
commit a5c50473a3
7 changed files with 135 additions and 117 deletions

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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());

View File

@@ -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);

View File

@@ -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>);
}
}