method refs: add constraint P1->ReferenceType for inexact method references despite of spec (IDEA-117311)

This commit is contained in:
Anna Kozlova
2014-01-29 20:58:06 +04:00
parent 9ef2c46b28
commit e2c708dfcf
4 changed files with 66 additions and 27 deletions

View File

@@ -37,7 +37,7 @@ public class PsiMethodReferenceUtil {
public static boolean hasReceiver(PsiType[] parameterTypes, QualifierResolveResult qualifierResolveResult, PsiMethodReferenceExpression methodRef) {
if (parameterTypes.length > 0 &&
isReceiverType(parameterTypes[0], qualifierResolveResult.getContainingClass(), qualifierResolveResult.getSubstitutor()) &&
isReceiverType(parameterTypes[0], qualifierResolveResult.getContainingClass(), qualifierResolveResult.getSubstitutor()) &&
isStaticallyReferenced(methodRef)) {
return true;
}
@@ -257,7 +257,6 @@ public class PsiMethodReferenceUtil {
}
public static boolean isReceiverType(PsiType receiverType, @Nullable PsiClass containingClass, PsiSubstitutor psiSubstitutor) {
boolean arrayType = receiverType instanceof PsiArrayType;
if (containingClass != null) {
receiverType = getExpandedType(receiverType, containingClass);
}
@@ -265,17 +264,15 @@ public class PsiMethodReferenceUtil {
final PsiClass receiverClass = resolveResult.getElement();
if (receiverClass != null && isReceiverType(receiverClass, containingClass)) {
LOG.assertTrue(containingClass != null);
return arrayType ||
resolveResult.getSubstitutor().equals(psiSubstitutor) ||
emptyOrRaw(containingClass, psiSubstitutor) ||
emptyOrRaw(receiverClass, resolveResult.getSubstitutor());
return emptyOrRaw(containingClass, psiSubstitutor) ||
TypeConversionUtil.isAssignable(JavaPsiFacade.getElementFactory(containingClass.getProject()).createType(containingClass, psiSubstitutor), GenericsUtil.eliminateWildcards(receiverType));
}
return false;
}
private static boolean emptyOrRaw(PsiClass containingClass, PsiSubstitutor psiSubstitutor) {
return PsiUtil.isRawSubstitutor(containingClass, psiSubstitutor) ||
(!containingClass.hasTypeParameters() && psiSubstitutor.getSubstitutionMap().isEmpty());
psiSubstitutor.getSubstitutionMap().isEmpty();
}
public static boolean isReceiverType(PsiType functionalInterfaceType, PsiClass containingClass, @Nullable PsiMethod referencedMethod) {

View File

@@ -20,8 +20,8 @@ import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
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.PsiMethodCallExpressionImpl;
import com.intellij.psi.impl.source.tree.java.PsiMethodReferenceExpressionImpl;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
@@ -81,25 +81,7 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
}
final PsiParameter[] parameters = applicableMember instanceof PsiMethod ? ((PsiMethod)applicableMember).getParameterList().getParameters() : PsiParameter.EMPTY_ARRAY;
if (targetParameters.length == parameters.length + 1) {
final PsiTypeElement qualifierTypeElement = myExpression.getQualifierType();
final PsiExpression qualifierExpression = myExpression.getQualifierExpression();
PsiType qualifierType;
if (qualifierTypeElement != null) {
qualifierType = qualifierTypeElement.getType();
}
else {
LOG.assertTrue(qualifierExpression != null);
qualifierType = qualifierExpression.getType();
if (qualifierType == null && qualifierExpression instanceof PsiReferenceExpression) {
final JavaResolveResult resolveResult = ((PsiReferenceExpression)qualifierExpression).advancedResolve(false);
final PsiElement resolve = resolveResult.getElement();
if (resolve instanceof PsiClass) {
qualifierType = JavaPsiFacade.getElementFactory(resolve.getProject()).createType((PsiClass)resolve, resolveResult.getSubstitutor());
}
}
}
constraints.add(new SubtypingConstraint(qualifierType, GenericsUtil.eliminateWildcards(substitutor.substitute(targetParameters[0].getType())), true));
specialCase(session, constraints, substitutor, targetParameters);
for (int i = 1; i < targetParameters.length; i++) {
constraints.add(new TypeCompatibilityConstraint(psiSubstitutor.substitute(parameters[i - 1].getType()), GenericsUtil.eliminateWildcards(substitutor.substitute(targetParameters[i].getType()))));
}
@@ -184,12 +166,50 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
session.initBounds(method.getTypeParameters());
session.initBounds(containingClass.getTypeParameters());
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (targetParameters.length == parameters.length + 1) {
specialCase(session, constraints, substitutor, targetParameters);
}
constraints.add(new TypeCompatibilityConstraint(returnType, referencedMethodReturnType));
}
return true;
}
private void specialCase(InferenceSession session,
List<ConstraintFormula> constraints,
PsiSubstitutor substitutor,
PsiParameter[] targetParameters) {
final PsiTypeElement qualifierTypeElement = myExpression.getQualifierType();
final PsiExpression qualifierExpression = myExpression.getQualifierExpression();
PsiType qualifierType;
if (qualifierTypeElement != null) {
qualifierType = qualifierTypeElement.getType();
}
else {
LOG.assertTrue(qualifierExpression != null);
qualifierType = qualifierExpression.getType();
if (qualifierType == null && qualifierExpression instanceof PsiReferenceExpression) {
final JavaResolveResult resolveResult = ((PsiReferenceExpression)qualifierExpression).advancedResolve(false);
final PsiElement res = resolveResult.getElement();
if (res instanceof PsiClass) {
PsiClass containingClass = (PsiClass)res;
final boolean isRawSubst = !myExpression.isConstructor() &&
PsiTreeUtil.isAncestor(containingClass, myExpression, true) &&
PsiUtil.isRawSubstitutor(containingClass, resolveResult.getSubstitutor());
qualifierType = JavaPsiFacade.getElementFactory(res.getProject()).createType(containingClass, isRawSubst ? PsiSubstitutor.EMPTY : resolveResult.getSubstitutor());
}
}
}
final PsiClass qualifierClass = PsiUtil.resolveClassInType(qualifierType);
if (qualifierClass != null) {
session.initBounds(qualifierClass.getTypeParameters());
constraints.add(new SubtypingConstraint(qualifierType, GenericsUtil.eliminateWildcards(substitutor.substitute(targetParameters[0].getType())), true));
}
}
@Override
public void apply(PsiSubstitutor substitutor) {
myT = substitutor.substitute(myT);

View File

@@ -0,0 +1,18 @@
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Box<TBox>
{
public TBox getValue()
{
return null;
}
void foo(Stream<Box<String>> stream){
List<String> l1 = stream.map(Box<String>::getValue).collect(Collectors.toList());
List<String> l2 = stream.map(Box::getValue).collect(Collectors.toList());
}
}

View File

@@ -129,6 +129,10 @@ public class NewMethodRefHighlightingTest extends LightDaemonAnalyzerTestCase {
doTest();
}
public void testIDEA117311() throws Exception {
doTest();
}
private void doTest() {
doTest(false);
}