mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
lambda: do not even try to infer from return value when value depends on params
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-19
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+15
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user