lambda: do not even try to infer from return value when value depends on params

This commit is contained in:
Anna Kozlova
2012-08-27 15:20:21 +04:00
parent bcb9a30c8b
commit 9c4504cfdf
3 changed files with 62 additions and 35 deletions
@@ -22,10 +22,7 @@ import com.intellij.psi.util.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* User: anna
@@ -259,9 +256,18 @@ public class LambdaUtil {
}
public static boolean dependsOnTypeParams(PsiType type, PsiLambdaExpression expr) {
return dependsOnTypeParams(type, expr, null);
}
public static boolean dependsOnTypeParams(PsiType type,
PsiLambdaExpression expr,
PsiTypeParameter param2Check) {
final TypeParamsChecker visitor = new TypeParamsChecker(expr);
if (!visitor.startedInference()) return false;
final Boolean accept = type.accept(visitor);
if (param2Check != null) {
return visitor.used(param2Check);
}
return accept != null && accept.booleanValue();
}
@@ -393,12 +399,11 @@ public class LambdaUtil {
}
private static class TypeParamsChecker extends PsiTypeVisitor<Boolean> {
private final PsiLambdaExpression myExpression;
private PsiMethod myMethod;
protected final PsiClass myClass;
private final PsiClass myClass;
private final Set<PsiTypeParameter> myUsedTypeParams = new HashSet<PsiTypeParameter>();
public TypeParamsChecker(PsiLambdaExpression expression) {
myExpression = expression;
myClass = PsiUtil.resolveGenericsClassInType(getFunctionalInterfaceType(expression, false)).getElement();
PsiElement parent = expression.getParent();
while (parent instanceof PsiParenthesizedExpression) {
@@ -418,21 +423,20 @@ public class LambdaUtil {
@Override
public Boolean visitClassType(PsiClassType classType) {
boolean used = false;
for (PsiType paramType : classType.getParameters()) {
final Boolean paramAccepted = paramType.accept(this);
if (paramAccepted != null && paramAccepted.booleanValue()) return true;
used |= paramAccepted != null && paramAccepted.booleanValue();
}
final PsiClass resolve = classType.resolve();
if (resolve instanceof PsiTypeParameter) {
final PsiTypeParameterListOwner owner = ((PsiTypeParameter)resolve).getOwner();
if (owner == myMethod) {
return true;
}
else if (owner == myClass) {
final PsiTypeParameter typeParameter = (PsiTypeParameter)resolve;
if (check(typeParameter)) {
myUsedTypeParams.add(typeParameter);
return true;
}
}
return false;
return used;
}
@Nullable
@@ -465,5 +469,20 @@ public class LambdaUtil {
public Boolean visitType(PsiType type) {
return false;
}
private boolean check(PsiTypeParameter check) {
final PsiTypeParameterListOwner owner = check.getOwner();
if (owner == myMethod) {
return true;
}
else if (owner == myClass) {
return true;
}
return false;
}
public boolean used(PsiTypeParameter parameter) {
return myUsedTypeParams.contains(parameter);
}
}
}
@@ -19,7 +19,6 @@ import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Pair;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.java.PsiLambdaExpressionImpl;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.scope.MethodProcessorSetupFailedException;
@@ -594,7 +593,7 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
final PsiParameter[] methodParameters = method.getParameterList().getParameters();
final PsiSubstitutor subst = resolveResult.getSubstitutor();
final boolean methodParamsDependOnTypeParams = methodParamsDependOnTypeParams(lambdaExpression, methodParameters, subst);
final boolean methodParamsDependOnTypeParams = methodParamsDependOnTypeParams(lambdaExpression, methodParameters, subst, typeParam);
final PsiType returnType = subst.substitute(method.getReturnType());
if (returnType != null && returnType != PsiType.VOID) {
Pair<PsiType, ConstraintType> constraint = null;
@@ -602,15 +601,13 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
for (final PsiExpression expression : expressions) {
final boolean independent = LambdaUtil.isFreeFromTypeInferenceArgs(methodParameters, lambdaExpression, expression);
if (independent && methodParamsDependOnTypeParams) return FAILED_INFERENCE;
if (!independent && lowerBound != PsiType.NULL) {
return null;
}
PsiType exprType = independent ? expression.getType() : ourGuard.doPreventingRecursion(lambdaExpression, true, new Computable<PsiType>() {
@Override
public PsiType compute() {
return expression.getType();
if (!independent) {
if (lowerBound != PsiType.NULL) {
return null;
}
});
continue;
}
PsiType exprType = expression.getType();
if (exprType instanceof PsiLambdaParameterType) {
final PsiParameter parameter = ((PsiLambdaParameterType)exprType).getParameter();
final int parameterIndex = lambdaExpression.getParameterList().getParameterIndex(parameter);
@@ -640,20 +637,17 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
}
if (constraint != null) return constraint;
}
for (PsiParameter parameter : methodParameters) {
if (LambdaUtil.dependsOnTypeParams(parameter.getType(), lambdaExpression)) {
return getFailedInferenceConstraint(typeParam);
}
}
if (methodParamsDependOnTypeParams) return getFailedInferenceConstraint(typeParam);
}
return null;
}
private static boolean methodParamsDependOnTypeParams(PsiLambdaExpression lambdaExpression,
PsiParameter[] methodParameters,
PsiSubstitutor subst) {
PsiSubstitutor subst,
PsiTypeParameter typeParam) {
for (PsiParameter parameter : methodParameters) {
if (LambdaUtil.dependsOnTypeParams(subst.substitute(parameter.getType()), lambdaExpression)) {
if (LambdaUtil.dependsOnTypeParams(subst.substitute(parameter.getType()), lambdaExpression, typeParam)) {
return true;
}
}
@@ -896,8 +890,9 @@ public class PsiResolveHelperImpl implements PsiResolveHelper {
if (functionalInterfaceType == null || PsiUtil.resolveClassInType(functionalInterfaceType) == typeParameter){
return getFailedInferenceConstraint(typeParameter);
}
final PsiType functionalInterfaceReturnType = LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType);
if (((PsiLambdaExpression)expression).getParameterList().getParametersCount() > 0 && functionalInterfaceReturnType != PsiType.VOID) {
final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
if (method == null || methodParamsDependOnTypeParams((PsiLambdaExpression)expression, method.getParameterList().getParameters(),
substitutor, typeParameter)) {
return getFailedInferenceConstraint(typeParameter);
}
}
@@ -1,17 +1,30 @@
import java.lang.String;
interface I<A, B> {
B foo(A a);
}
class Foo<T> {
public <V> Foo<V> map(I<T, V> mapper) {
return new Foo<V>();
}
}
class NoInferenceResult {
<A, B> I<A, B> m(I<A, B> f) { return null; }
<T> void m1(T t) { }
void test() {
m(<error descr="Incompatible return type <lambda expression> in lambda expression">(String s1) -> (String s2) -> s1 + s2</error>);
m((String s1) -> <error descr="Target type of a lambda conversion must be an interface">(String s2) -> s1 + s2</error>);
m(<error descr="Incompatible return type <lambda expression> in lambda expression">(String s1) -> {return (String s2) -> s1 + s2;}</error>);
m((String s1) -> s1.length());
m((String s1) -> s1);
m1(<error descr="Cyclic inference">() -> { }</error>);
m1(<error descr="Cyclic inference">() -> { }</error>);
Foo<String> foo = new Foo<String>();
foo.map(v -> null);
Foo<String> map1 = foo.map(value -> value + ", " + value);
}
}