mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-30 02:09:59 +07:00
applicability constraints by method reference (IDEA-122018)
This commit is contained in:
@@ -23,10 +23,7 @@ import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.resolve.graphInference.constraints.*;
|
||||
import com.intellij.psi.infos.MethodCandidateInfo;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.ArrayUtilRt;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.Processor;
|
||||
@@ -374,7 +371,7 @@ public class InferenceSession {
|
||||
if (!PsiType.VOID.equals(returnType) && returnType != null) {
|
||||
PsiType targetType = getTargetType(context);
|
||||
if (targetType != null) {
|
||||
registerConstraints(PsiUtil.isRawSubstitutor(method, mySiteSubstitutor) ? returnType : mySiteSubstitutor.substitute(returnType), targetType);
|
||||
registerReturnTypeConstraints(PsiUtil.isRawSubstitutor(method, mySiteSubstitutor) ? returnType : mySiteSubstitutor.substitute(returnType), targetType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -387,7 +384,7 @@ public class InferenceSession {
|
||||
}
|
||||
}
|
||||
|
||||
public void registerConstraints(PsiType returnType, PsiType targetType) {
|
||||
public void registerReturnTypeConstraints(PsiType returnType, PsiType targetType) {
|
||||
final InferenceVariable inferenceVariable = shouldResolveAndInstantiate(returnType, targetType);
|
||||
if (inferenceVariable != null) {
|
||||
final PsiSubstitutor substitutor = resolveSubset(Collections.singletonList(inferenceVariable), mySiteSubstitutor);
|
||||
@@ -920,6 +917,76 @@ public class InferenceSession {
|
||||
return subset;
|
||||
}
|
||||
|
||||
public PsiSubstitutor collectApplicabilityConstraints(final PsiMethodReferenceExpression reference,
|
||||
final MethodCandidateInfo candidateInfo,
|
||||
final PsiType functionalInterfaceType) {
|
||||
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
|
||||
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
|
||||
LOG.assertTrue(interfaceMethod != null, myContext);
|
||||
final PsiSubstitutor functionalInterfaceSubstitutor = LambdaUtil.getSubstitutor(interfaceMethod, resolveResult);
|
||||
final MethodSignature signature = interfaceMethod.getSignature(functionalInterfaceSubstitutor);
|
||||
|
||||
final boolean varargs = candidateInfo.isVarargs();
|
||||
final PsiMethod method = candidateInfo.getElement();
|
||||
|
||||
final PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult = PsiMethodReferenceUtil.getQualifierResolveResult(reference);
|
||||
|
||||
final PsiClass containingClass = qualifierResolveResult.getContainingClass();
|
||||
LOG.assertTrue(containingClass != null, myContext);
|
||||
|
||||
final PsiParameter[] functionalMethodParameters = interfaceMethod.getParameterList().getParameters();
|
||||
final PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
|
||||
final boolean isStatic = method.hasModifierProperty(PsiModifier.STATIC);
|
||||
|
||||
if (parameters.length == functionalMethodParameters.length && !varargs || isStatic && varargs) {//static methods
|
||||
|
||||
if (method.isConstructor() && PsiUtil.isRawSubstitutor(containingClass, qualifierResolveResult.getSubstitutor())) {
|
||||
initBounds(containingClass.getTypeParameters());
|
||||
}
|
||||
|
||||
for (int i = 0; i < functionalMethodParameters.length; i++) {
|
||||
final PsiType pType = signature.getParameterTypes()[i];
|
||||
addConstraint(new TypeCompatibilityConstraint(getParameterType(parameters, i, PsiSubstitutor.EMPTY, varargs), pType));
|
||||
}
|
||||
}
|
||||
else if (parameters.length + 1 == functionalMethodParameters.length && !varargs || !isStatic && varargs && functionalMethodParameters.length > 0) { //instance methods
|
||||
initBounds(containingClass.getTypeParameters());
|
||||
|
||||
final PsiType pType = signature.getParameterTypes()[0];
|
||||
|
||||
PsiSubstitutor psiSubstitutor = qualifierResolveResult.getSubstitutor();
|
||||
// 15.28.1 If the ReferenceType is a raw type, and there exists a parameterization of this type, T, that is a supertype of P1,
|
||||
// the type to search is the result of capture conversion (5.1.10) applied to T;
|
||||
// otherwise, the type to search is the same as the type of the first search. Again, the type arguments, if any, are given by the method reference.
|
||||
if (PsiUtil.isRawSubstitutor(containingClass, qualifierResolveResult.getSubstitutor())) {
|
||||
final PsiClassType.ClassResolveResult pResult = PsiUtil.resolveGenericsClassInType(pType);
|
||||
final PsiClass pClass = pResult.getElement();
|
||||
final PsiSubstitutor receiverSubstitutor = pClass != null ? TypeConversionUtil
|
||||
.getClassSubstitutor(containingClass, pClass, pResult.getSubstitutor()) : null;
|
||||
if (receiverSubstitutor != null) {
|
||||
if (!method.hasTypeParameters()) {
|
||||
if (signature.getParameterTypes().length == 1 || PsiUtil.isRawSubstitutor(containingClass, receiverSubstitutor)) {
|
||||
return receiverSubstitutor;
|
||||
}
|
||||
}
|
||||
psiSubstitutor = receiverSubstitutor;
|
||||
}
|
||||
}
|
||||
|
||||
final PsiType qType = JavaPsiFacade.getElementFactory(method.getProject()).createType(containingClass, psiSubstitutor);
|
||||
|
||||
addConstraint(new TypeCompatibilityConstraint(qType, pType));
|
||||
|
||||
for (int i = 0; i < signature.getParameterTypes().length - 1; i++) {
|
||||
final PsiType interfaceParamType = signature.getParameterTypes()[i + 1];
|
||||
addConstraint(new TypeCompatibilityConstraint(getParameterType(parameters, i, PsiSubstitutor.EMPTY, varargs), interfaceParamType));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setErased() {
|
||||
myErased = true;
|
||||
}
|
||||
@@ -949,8 +1016,8 @@ public class InferenceSession {
|
||||
|
||||
final int paramsLength = !varargs ? parameters1.length : parameters1.length - 1;
|
||||
for (int i = 0; i < paramsLength; i++) {
|
||||
PsiType sType = siteSubstitutor2.substitute(parameters1[i].getType());
|
||||
PsiType tType = siteSubstitutor2.substitute(getVarargParameterType(varargs, i, parameters2));
|
||||
PsiType sType = getParameterType(parameters1, i, siteSubstitutor2, false);
|
||||
PsiType tType = getParameterType(parameters2, i, siteSubstitutor2, varargs);
|
||||
if (session.isProperType(sType) && session.isProperType(tType)) {
|
||||
if (!TypeConversionUtil.isAssignable(tType, sType)) {
|
||||
return false;
|
||||
@@ -967,24 +1034,14 @@ public class InferenceSession {
|
||||
}
|
||||
|
||||
if (varargs) {
|
||||
PsiType sType = siteSubstitutor2.substitute(getVarargParameterType(true, paramsLength, parameters1));
|
||||
PsiType tType = siteSubstitutor2.substitute(getVarargParameterType(true, paramsLength, parameters2));
|
||||
PsiType sType = getParameterType(parameters1, paramsLength, siteSubstitutor2, true);
|
||||
PsiType tType = getParameterType(parameters2, paramsLength, siteSubstitutor2, true);
|
||||
session.addConstraint(new StrictSubtypingConstraint(tType, sType));
|
||||
}
|
||||
|
||||
return session.repeatInferencePhases(true);
|
||||
}
|
||||
|
||||
public static PsiType getVarargParameterType(boolean varargs, int i, PsiParameter[] parameters2) {
|
||||
if (varargs && i >= parameters2.length - 1) {
|
||||
final PsiType lastParamType = parameters2[parameters2.length - 1].getType();
|
||||
if (lastParamType instanceof PsiEllipsisType) {
|
||||
return ((PsiEllipsisType)lastParamType).getComponentType();
|
||||
}
|
||||
}
|
||||
return parameters2[i].getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* 15.12.2.5 Choosing the Most Specific Method
|
||||
* "a functional interface type S is more specific than a functional interface type T for an expression exp" part
|
||||
|
||||
@@ -145,7 +145,9 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
|
||||
if (!accepted) {
|
||||
return false;
|
||||
}
|
||||
callSession.registerConstraints(method != null && !PsiUtil.isRawSubstitutor(method, siteSubstitutor) ? siteSubstitutor.substitute(returnType) : returnType, substitutor.substitute(returnType));
|
||||
callSession.registerReturnTypeConstraints(
|
||||
method != null && !PsiUtil.isRawSubstitutor(method, siteSubstitutor) ? siteSubstitutor.substitute(returnType) : returnType,
|
||||
substitutor.substitute(returnType));
|
||||
if (callSession.repeatInferencePhases(true)) {
|
||||
final Collection<InferenceVariable> inferenceVariables = callSession.getInferenceVariables();
|
||||
if (sameMethodCall) {
|
||||
|
||||
@@ -20,7 +20,7 @@ import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil;
|
||||
import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
|
||||
import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil;
|
||||
import com.intellij.psi.impl.source.tree.java.PsiMethodReferenceExpressionImpl;
|
||||
import com.intellij.psi.infos.MethodCandidateInfo;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
@@ -68,7 +68,7 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
|
||||
} else {
|
||||
final PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult = PsiMethodReferenceUtil.getQualifierResolveResult(myExpression);
|
||||
PsiSubstitutor psiSubstitutor = qualifierResolveResult.getSubstitutor();
|
||||
final PsiMember applicableMember = ((PsiMethodReferenceExpressionImpl)myExpression).getPotentiallyApplicableMember();
|
||||
final PsiMember applicableMember = myExpression.getPotentiallyApplicableMember();
|
||||
LOG.assertTrue(applicableMember != null);
|
||||
PsiType applicableMethodReturnType = applicableMember instanceof PsiMethod ? ((PsiMethod)applicableMember).getReturnType() : null;
|
||||
int idx = 0;
|
||||
@@ -111,16 +111,17 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
|
||||
|
||||
final Map<PsiMethodReferenceExpression, PsiType> map = PsiMethodReferenceUtil.getFunctionalTypeMap();
|
||||
final PsiType added = map.put(myExpression, groundTargetType);
|
||||
final PsiElement resolve;
|
||||
final JavaResolveResult resolve;
|
||||
try {
|
||||
resolve = myExpression.resolve();
|
||||
resolve = myExpression.advancedResolve(true);
|
||||
}
|
||||
finally {
|
||||
if (added == null) {
|
||||
map.remove(myExpression);
|
||||
}
|
||||
}
|
||||
if (resolve == null) {
|
||||
final PsiElement element = resolve.getElement();
|
||||
if (element == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -128,8 +129,8 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
|
||||
return true;
|
||||
}
|
||||
|
||||
if (resolve instanceof PsiMethod) {
|
||||
final PsiMethod method = (PsiMethod)resolve;
|
||||
if (element instanceof PsiMethod) {
|
||||
final PsiMethod method = (PsiMethod)element;
|
||||
final PsiType referencedMethodReturnType;
|
||||
final PsiClass containingClass = method.getContainingClass();
|
||||
LOG.assertTrue(containingClass != null, method);
|
||||
@@ -158,8 +159,8 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
|
||||
ContainerUtil.newHashSet(method.getTypeParameters()))) {
|
||||
//the constraint reduces to the bound set B3 which would be used to determine the method reference's invocation type
|
||||
//when targeting the return type of the function type, as defined in 18.5.2.
|
||||
//as there is no parameters, only constraint for return types is left. Here you are:
|
||||
session.registerConstraints(referencedMethodReturnType, returnType);
|
||||
session.collectApplicabilityConstraints(myExpression, ((MethodCandidateInfo)resolve), groundTargetType);
|
||||
session.registerReturnTypeConstraints(referencedMethodReturnType, returnType);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import com.intellij.psi.impl.source.resolve.ParameterTypeInferencePolicy;
|
||||
import com.intellij.psi.impl.source.resolve.ResolveCache;
|
||||
import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil;
|
||||
import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession;
|
||||
import com.intellij.psi.impl.source.resolve.graphInference.constraints.TypeCompatibilityConstraint;
|
||||
import com.intellij.psi.infos.CandidateInfo;
|
||||
import com.intellij.psi.infos.ClassCandidateInfo;
|
||||
import com.intellij.psi.infos.MethodCandidateInfo;
|
||||
@@ -41,7 +40,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
class MethodReferenceResolver implements ResolveCache.PolyVariantContextResolver<PsiMethodReferenceExpressionImpl> {
|
||||
public class MethodReferenceResolver implements ResolveCache.PolyVariantContextResolver<PsiMethodReferenceExpressionImpl> {
|
||||
private static final Logger LOG = Logger.getInstance("#" + MethodReferenceResolver.class.getName());
|
||||
|
||||
@NotNull
|
||||
@@ -70,7 +69,7 @@ class MethodReferenceResolver implements ResolveCache.PolyVariantContextResolver
|
||||
final PsiClassType returnType = composeReturnType(containingClass, substitutor);
|
||||
final InferenceSession session = new InferenceSession(containingClass.getTypeParameters(), substitutor, reference.getManager(), null);
|
||||
if (!(session.isProperType(returnType) && session.isProperType(interfaceMethodReturnType))) {
|
||||
session.registerConstraints(returnType, interfaceMethodReturnType);
|
||||
session.registerReturnTypeConstraints(returnType, interfaceMethodReturnType);
|
||||
substitutor = session.infer();
|
||||
}
|
||||
}
|
||||
@@ -111,12 +110,11 @@ class MethodReferenceResolver implements ResolveCache.PolyVariantContextResolver
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiSubstitutor inferTypeArguments(@NotNull ParameterTypeInferencePolicy policy, boolean includeReturnConstraint) {
|
||||
return inferTypeArguments(varargs);
|
||||
return inferTypeArguments();
|
||||
}
|
||||
|
||||
public PsiSubstitutor inferTypeArguments(boolean varargs) {
|
||||
private PsiSubstitutor inferTypeArguments() {
|
||||
if (interfaceMethod == null) return substitutor;
|
||||
final PsiSubstitutor qualifierResultSubstitutor = qualifierResolveResult.getSubstitutor();
|
||||
final InferenceSession session = new InferenceSession(method.getTypeParameters(), substitutor, reference.getManager(), reference);
|
||||
|
||||
//lift parameters from outer call
|
||||
@@ -125,53 +123,9 @@ class MethodReferenceResolver implements ResolveCache.PolyVariantContextResolver
|
||||
session.initBounds(methodSubstitutorPair.getMethod().getTypeParameters());
|
||||
}
|
||||
|
||||
final PsiParameter[] functionalMethodParameters = interfaceMethod.getParameterList().getParameters();
|
||||
final PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
final boolean isStatic = method.hasModifierProperty(PsiModifier.STATIC);
|
||||
if (parameters.length == functionalMethodParameters.length && !varargs || isStatic && varargs) {//static methods
|
||||
|
||||
if (method.isConstructor() && PsiUtil.isRawSubstitutor(containingClass, qualifierResultSubstitutor)) {
|
||||
session.initBounds(containingClass.getTypeParameters());
|
||||
}
|
||||
|
||||
for (int i = 0; i < functionalMethodParameters.length; i++) {
|
||||
final PsiType pType = signature.getParameterTypes()[i];
|
||||
session.addConstraint(new TypeCompatibilityConstraint(getParameterType(parameters, i, varargs), pType));
|
||||
}
|
||||
}
|
||||
else if (parameters.length + 1 == functionalMethodParameters.length && !varargs || !isStatic && varargs && functionalMethodParameters.length > 0) { //instance methods
|
||||
final PsiClass aClass = qualifierResolveResult.getContainingClass();
|
||||
session.initBounds(aClass.getTypeParameters());
|
||||
|
||||
final PsiType pType = signature.getParameterTypes()[0];
|
||||
|
||||
PsiSubstitutor psiSubstitutor = qualifierResultSubstitutor;
|
||||
// 15.28.1 If the ReferenceType is a raw type, and there exists a parameterization of this type, T, that is a supertype of P1,
|
||||
// the type to search is the result of capture conversion (5.1.10) applied to T;
|
||||
// otherwise, the type to search is the same as the type of the first search. Again, the type arguments, if any, are given by the method reference.
|
||||
if (PsiUtil.isRawSubstitutor(containingClass, qualifierResultSubstitutor)) {
|
||||
final PsiClassType.ClassResolveResult pResult = PsiUtil.resolveGenericsClassInType(pType);
|
||||
final PsiClass pClass = pResult.getElement();
|
||||
final PsiSubstitutor receiverSubstitutor = pClass != null ? TypeConversionUtil
|
||||
.getClassSubstitutor(containingClass, pClass, pResult.getSubstitutor()) : null;
|
||||
if (receiverSubstitutor != null) {
|
||||
if (!method.hasTypeParameters()) {
|
||||
if (signature.getParameterTypes().length == 1 || PsiUtil.isRawSubstitutor(containingClass, receiverSubstitutor)) {
|
||||
return receiverSubstitutor;
|
||||
}
|
||||
}
|
||||
psiSubstitutor = receiverSubstitutor;
|
||||
}
|
||||
}
|
||||
|
||||
final PsiType qType = JavaPsiFacade.getElementFactory(reference.getProject()).createType(containingClass, psiSubstitutor);
|
||||
|
||||
session.addConstraint(new TypeCompatibilityConstraint(qType, pType));
|
||||
|
||||
for (int i = 0; i < signature.getParameterTypes().length - 1; i++) {
|
||||
final PsiType interfaceParamType = signature.getParameterTypes()[i + 1];
|
||||
session.addConstraint(new TypeCompatibilityConstraint(getParameterType(parameters, i, varargs), interfaceParamType));
|
||||
}
|
||||
final PsiSubstitutor psiSubstitutor = session.collectApplicabilityConstraints(reference, this, functionalInterfaceType);
|
||||
if (psiSubstitutor != null) {
|
||||
return psiSubstitutor;
|
||||
}
|
||||
|
||||
if (!session.repeatInferencePhases(false)) {
|
||||
@@ -181,19 +135,10 @@ class MethodReferenceResolver implements ResolveCache.PolyVariantContextResolver
|
||||
if (interfaceMethodReturnType != PsiType.VOID && interfaceMethodReturnType != null) {
|
||||
final PsiType returnType = method.isConstructor() ? composeReturnType(containingClass, substitutor) : method.getReturnType();
|
||||
if (returnType != null) {
|
||||
session.registerConstraints(returnType, interfaceMethodReturnType);
|
||||
session.registerReturnTypeConstraints(returnType, interfaceMethodReturnType);
|
||||
}
|
||||
}
|
||||
return session.infer(parameters, null, null);
|
||||
}
|
||||
|
||||
private PsiType getParameterType(PsiParameter[] parameters, int i, boolean varargs) {
|
||||
if (varargs && i >= parameters.length - 1) {
|
||||
final PsiType type = parameters[parameters.length - 1].getType();
|
||||
LOG.assertTrue(type instanceof PsiEllipsisType);
|
||||
return ((PsiEllipsisType)type).getComponentType();
|
||||
}
|
||||
return parameters[i].getType();
|
||||
return session.infer(method.getParameterList().getParameters(), null, null);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -254,8 +199,7 @@ class MethodReferenceResolver implements ResolveCache.PolyVariantContextResolver
|
||||
|
||||
private static PsiClassType composeReturnType(PsiClass containingClass, PsiSubstitutor substitutor) {
|
||||
final boolean isRawSubst = PsiUtil.isRawSubstitutor(containingClass, substitutor);
|
||||
return JavaPsiFacade.getElementFactory(containingClass.getProject())
|
||||
.createType(containingClass, isRawSubst ? PsiSubstitutor.EMPTY : substitutor);
|
||||
return JavaPsiFacade.getElementFactory(containingClass.getProject()).createType(containingClass, isRawSubst ? PsiSubstitutor.EMPTY : substitutor);
|
||||
}
|
||||
|
||||
private static class MethodReferenceConflictResolver extends JavaMethodsConflictResolver {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
class Base {
|
||||
|
||||
interface Seq<E> extends Iterable<E> {
|
||||
static <E> Seq<E> of(Iterable<? extends E> source) {
|
||||
return null;
|
||||
}
|
||||
|
||||
<R> Seq<R> map(Function<? super E, ? extends R> mapper);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Test3 extends Base {
|
||||
|
||||
static void test3(Seq<String[]> many) {
|
||||
Seq<Seq<String>> mapped = many.map(Arrays::asList).map(Seq::of);
|
||||
Seq<Seq<String>> mappedL = many.map(Arrays::asList).map(list -> Seq.of(list));
|
||||
}
|
||||
|
||||
public static <T> List<T> asList(T[] a) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -249,6 +249,10 @@ public class NewMethodRefHighlightingTest extends LightDaemonAnalyzerTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testCollectingApplicabilityConstraints() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTest() {
|
||||
doTest(false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user