mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
new inference: wildcard parameterization inference
This commit is contained in:
+31
-13
@@ -47,12 +47,12 @@ public class FunctionalInterfaceParameterizationUtil {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiType getGroundTargetType(@Nullable PsiType psiClassType, PsiLambdaExpression expr, boolean resolve) {
|
||||
public static PsiType getGroundTargetType(@Nullable PsiType psiClassType, PsiLambdaExpression expr) {
|
||||
if (!isWildcardParameterized(psiClassType)) {
|
||||
return psiClassType;
|
||||
}
|
||||
|
||||
if (expr.hasFormalParameterTypes()) return getFunctionalTypeExplicit(psiClassType, expr, resolve);
|
||||
if (expr.hasFormalParameterTypes()) return getFunctionalTypeExplicit(psiClassType, expr);
|
||||
|
||||
return getFunctionalTypeImplicit(psiClassType);
|
||||
}
|
||||
@@ -90,11 +90,11 @@ public class FunctionalInterfaceParameterizationUtil {
|
||||
return psiClassType;
|
||||
}
|
||||
|
||||
private static PsiType getFunctionalTypeExplicit(PsiType psiClassType, PsiLambdaExpression expr, boolean resolve) {
|
||||
private static PsiType getFunctionalTypeExplicit(PsiType psiClassType, PsiLambdaExpression expr) {
|
||||
final PsiParameter[] lambdaParams = expr.getParameterList().getParameters();
|
||||
if (psiClassType instanceof PsiIntersectionType) {
|
||||
for (PsiType psiType : ((PsiIntersectionType)psiClassType).getConjuncts()) {
|
||||
final PsiType functionalType = getFunctionalTypeExplicit(psiType, expr, false);
|
||||
final PsiType functionalType = getFunctionalTypeExplicit(psiType, expr);
|
||||
if (functionalType != null) return functionalType;
|
||||
}
|
||||
return null;
|
||||
@@ -110,28 +110,46 @@ public class FunctionalInterfaceParameterizationUtil {
|
||||
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
|
||||
if (interfaceMethod == null) return null;
|
||||
|
||||
final InferenceSession session = new InferenceSession(PsiSubstitutor.EMPTY, expr);
|
||||
PsiTypeParameter[] typeParameters = psiClass.getTypeParameters();
|
||||
if (typeParameters.length != parameters.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < typeParameters.length; i++) {
|
||||
session.addVariable(typeParameters[i], parameters[i]);
|
||||
}
|
||||
|
||||
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(psiClass.getProject());
|
||||
final PsiParameter[] targetMethodParams = interfaceMethod.getParameterList().getParameters();
|
||||
if (targetMethodParams.length != lambdaParams.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final InferenceSession session = new InferenceSession(typeParameters, PsiSubstitutor.EMPTY, expr.getManager(), expr);
|
||||
|
||||
for (int i = 0; i < targetMethodParams.length; i++) {
|
||||
if (resolve) {
|
||||
session.addConstraint(new TypeEqualityConstraint(lambdaParams[i].getType(), targetMethodParams[i].getType()));
|
||||
session.addConstraint(new TypeEqualityConstraint(lambdaParams[i].getType(), targetMethodParams[i].getType()));
|
||||
}
|
||||
|
||||
if (!session.repeatInferencePhases(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final PsiSubstitutor substitutor = session.resolveBounds(true);
|
||||
|
||||
final PsiType[] newTypeParameters = new PsiType[parameters.length];
|
||||
for (int i = 0; i < typeParameters.length; i++) {
|
||||
PsiTypeParameter typeParameter = typeParameters[i];
|
||||
final InferenceVariable variable = session.getInferenceVariable(typeParameter);
|
||||
final PsiType instantiation = variable.getInstantiation();
|
||||
if (instantiation != PsiType.NULL) {
|
||||
newTypeParameters[i] = instantiation;
|
||||
} else {
|
||||
newTypeParameters[i] = parameters[i];
|
||||
}
|
||||
}
|
||||
|
||||
final PsiClassType parameterization = elementFactory.createType(psiClass, session.infer());
|
||||
final PsiClassType parameterization = elementFactory.createType(psiClass, newTypeParameters);
|
||||
|
||||
if (//todo !TypeConversionUtil.isAssignable(psiClassType, parameterization) ||
|
||||
!GenericsUtil.isTypeArgumentsApplicable(typeParameters, PsiSubstitutor.EMPTY.putAll(psiClass, newTypeParameters), expr)) {
|
||||
return null;
|
||||
}
|
||||
if (!TypeConversionUtil.containsWildcards(parameterization)) return parameterization;
|
||||
}
|
||||
return null;
|
||||
|
||||
+15
-22
@@ -60,11 +60,6 @@ public class InferenceSession {
|
||||
|
||||
private final PsiElement myContext;
|
||||
|
||||
public InferenceSession(PsiSubstitutor siteSubstitutor, PsiElement expr) {
|
||||
mySiteSubstitutor = siteSubstitutor;
|
||||
myContext = expr;
|
||||
}
|
||||
|
||||
public InferenceSession(PsiTypeParameter[] typeParams,
|
||||
PsiType[] leftTypes,
|
||||
PsiType[] rightTypes,
|
||||
@@ -205,6 +200,7 @@ public class InferenceSession {
|
||||
@Nullable PsiElement parent,
|
||||
ParameterTypeInferencePolicy policy) {
|
||||
boolean doesNotContainFalseBound = repeatInferencePhases(parameters == null);
|
||||
// if (!doesNotContainFalseBound) return prepareSubstitution();
|
||||
|
||||
resolveBounds(myInferenceVariables.values(), mySiteSubstitutor, false);
|
||||
|
||||
@@ -215,6 +211,7 @@ public class InferenceSession {
|
||||
inferenceVariable.ignoreInstantiation();
|
||||
}
|
||||
doesNotContainFalseBound = repeatInferencePhases(true);
|
||||
// if (!doesNotContainFalseBound) return prepareSubstitution();
|
||||
|
||||
PsiSubstitutor substitutor = resolveBounds(myInferenceVariables.values(), mySiteSubstitutor, !policy.allowPostponeInference());
|
||||
LOG.assertTrue(parent != null);
|
||||
@@ -242,6 +239,7 @@ public class InferenceSession {
|
||||
inferenceVariable.ignoreInstantiation();
|
||||
}
|
||||
doesNotContainFalseBound = proceedWithAdditionalConstraints(additionalConstraints);
|
||||
// if (!doesNotContainFalseBound) return prepareSubstitution();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,6 +248,10 @@ public class InferenceSession {
|
||||
}
|
||||
mySiteSubstitutor = resolveBounds(myInferenceVariables.values(), mySiteSubstitutor, !policy.allowPostponeInference());
|
||||
|
||||
return prepareSubstitution();
|
||||
}
|
||||
|
||||
private PsiSubstitutor prepareSubstitution() {
|
||||
for (InferenceVariable inferenceVariable : myInferenceVariables.values()) {
|
||||
final PsiTypeParameter typeParameter = inferenceVariable.getParameter();
|
||||
PsiType instantiation = inferenceVariable.getInstantiation();
|
||||
@@ -572,7 +574,7 @@ public class InferenceSession {
|
||||
return dependencies != null ? !dependencies.isEmpty() : isProper;
|
||||
}
|
||||
|
||||
private boolean repeatInferencePhases(boolean incorporate) {
|
||||
protected boolean repeatInferencePhases(boolean incorporate) {
|
||||
do {
|
||||
if (!reduceConstraints()) {
|
||||
//inference error occurred
|
||||
@@ -603,7 +605,13 @@ public class InferenceSession {
|
||||
return true;
|
||||
}
|
||||
|
||||
private PsiSubstitutor resolveBounds(final Collection<InferenceVariable> inferenceVariables, PsiSubstitutor substitutor, boolean acceptInitialUpperBound) {
|
||||
protected PsiSubstitutor resolveBounds(boolean acceptInitialUpperBound) {
|
||||
return resolveBounds(getInferenceVariables(), mySiteSubstitutor, acceptInitialUpperBound);
|
||||
}
|
||||
|
||||
private PsiSubstitutor resolveBounds(final Collection<InferenceVariable> inferenceVariables,
|
||||
PsiSubstitutor substitutor,
|
||||
boolean acceptInitialUpperBound) {
|
||||
final List<List<InferenceVariable>> independentVars = InferenceVariablesOrder.resolveOrder(inferenceVariables, this);
|
||||
for (List<InferenceVariable> variables : independentVars) {
|
||||
for (InferenceVariable inferenceVariable : variables) {
|
||||
@@ -734,21 +742,6 @@ public class InferenceSession {
|
||||
return myInferenceVariables.keySet();
|
||||
}
|
||||
|
||||
public void addVariable(PsiTypeParameter typeParameter, final PsiType parameter) {
|
||||
InferenceVariable variable = new InferenceVariable(typeParameter);
|
||||
if (parameter instanceof PsiWildcardType) {
|
||||
PsiType bound = ((PsiWildcardType)parameter).getBound();
|
||||
if (bound != null) {
|
||||
variable.addBound(bound, ((PsiWildcardType)parameter).isExtends() ? InferenceBound.UPPER : InferenceBound.LOWER);
|
||||
} else {
|
||||
variable.addBound(PsiType.getJavaLangObject(typeParameter.getManager(), typeParameter.getResolveScope()), InferenceBound.UPPER);
|
||||
}
|
||||
} else {
|
||||
variable.addBound(parameter, InferenceBound.EQ);
|
||||
}
|
||||
myInferenceVariables.put(typeParameter, variable);
|
||||
}
|
||||
|
||||
private boolean proceedWithAdditionalConstraints(Set<ConstraintFormula> additionalConstraints) {
|
||||
while (!additionalConstraints.isEmpty()) {
|
||||
final Set<InferenceVariable> outputVariables = new HashSet<InferenceVariable>();
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ public abstract class InputOutputConstraintFormula implements ConstraintFormula
|
||||
if (LambdaHighlightingUtil.checkInterfaceFunctional(type) == null) {
|
||||
final PsiType functionType =
|
||||
psiExpression instanceof PsiLambdaExpression
|
||||
? FunctionalInterfaceParameterizationUtil.getGroundTargetType(type, (PsiLambdaExpression)psiExpression, true)
|
||||
? FunctionalInterfaceParameterizationUtil.getGroundTargetType(type, (PsiLambdaExpression)psiExpression)
|
||||
: type;
|
||||
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionType);
|
||||
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
|
||||
|
||||
+1
-4
@@ -1,13 +1,10 @@
|
||||
package com.intellij.psi.impl.source.resolve.graphInference.constraints;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.controlFlow.*;
|
||||
import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil;
|
||||
import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.containers.IntArrayList;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -28,7 +25,7 @@ public class LambdaExpressionCompatibilityConstraint implements ConstraintFormul
|
||||
return false;
|
||||
}
|
||||
|
||||
final PsiType groundTargetType = FunctionalInterfaceParameterizationUtil.getGroundTargetType(myT, myExpression, false);
|
||||
final PsiType groundTargetType = FunctionalInterfaceParameterizationUtil.getGroundTargetType(myT, myExpression);
|
||||
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(groundTargetType);
|
||||
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
|
||||
if (interfaceMethod == null) {
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ public class PsiLambdaExpressionImpl extends ExpressionPsiElement implements Psi
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiType getFunctionalInterfaceType() {
|
||||
return FunctionalInterfaceParameterizationUtil.getGroundTargetType(LambdaUtil.getFunctionalInterfaceType(this, true), this, true);
|
||||
return FunctionalInterfaceParameterizationUtil.getGroundTargetType(LambdaUtil.getFunctionalInterfaceType(this, true), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ public class SampleExtendsWildcard {
|
||||
|
||||
class SampleSuperWildcard {
|
||||
public void highlightsTheBug(Stream<String> stream) {
|
||||
stream.flatMap((<error descr="Incompatible parameter types in lambda expression">Block<?> sink</error>, String element) -> {});
|
||||
stream.flatMap(<error descr="Cannot infer functional interface type">(Block<?> sink, String element) -> {}</error>);
|
||||
}
|
||||
|
||||
public interface Block<B> {
|
||||
|
||||
Reference in New Issue
Block a user