method references: fix type to search (site substitution) for ReferenceType::Identifier references, where ReferenceType is raw (IDEA-135792)

This commit is contained in:
Anna Kozlova
2015-01-30 22:28:02 +03:00
parent 26eb68f6fc
commit 8886622b5c
6 changed files with 58 additions and 63 deletions
@@ -297,7 +297,7 @@ public class GenericsUtil {
}
else if (substituted instanceof PsiCapturedWildcardType) {
final PsiType extendsBound = ((PsiCapturedWildcardType)substituted).getUpperBound();
if (acceptExtendsBound(extendsType, extendsBound)) {
if (acceptExtendsBound(extendsType, extendsBound) || extendsType.equals(substitutor.substitute(extendsBound))) {
return null;
}
}
@@ -175,10 +175,7 @@ public class PsiMethodReferenceUtil {
if (resolve instanceof PsiClass) {
containingClass = (PsiClass)resolve;
substitutor = resolveResult.getSubstitutor();
final boolean isRawSubst = !methodReferenceExpression.isConstructor() &&
PsiTreeUtil.isAncestor(containingClass, methodReferenceExpression, true) &&
PsiUtil.isRawSubstitutor(containingClass, substitutor);
return new QualifierResolveResult(containingClass, isRawSubst ? PsiSubstitutor.EMPTY : substitutor, true);
return new QualifierResolveResult(containingClass, substitutor, true);
}
}
}
@@ -1130,11 +1130,11 @@ public class InferenceSession {
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,
// 15.13.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);
if (PsiUtil.isRawSubstitutor(containingClass, psiSubstitutor)) {
final PsiClassType.ClassResolveResult pResult = PsiUtil.resolveGenericsClassInType(PsiUtil.captureToplevelWildcards(pType, myContext));
final PsiClass pClass = pResult.getElement();
final PsiSubstitutor receiverSubstitutor = pClass != null ? TypeConversionUtil
.getClassSubstitutor(containingClass, pClass, pResult.getSubstitutor()) : null;
@@ -1412,8 +1412,7 @@ public class InferenceSession {
}
public PsiType substituteWithInferenceVariables(PsiType type) {
final PsiType substituted = myInferenceSubstitution.substitute(type);
return isProperType(substituted) ? type : substituted;
return myInferenceSubstitution.substitute(type);
}
public InferenceSession findNestedCallSession(PsiExpression arg) {
@@ -63,16 +63,10 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
final PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult = PsiMethodReferenceUtil.getQualifierResolveResult(myExpression);
if (!myExpression.isExact()) {
for (PsiParameter parameter : targetParameters) {
if (!session.isProperType(substitutor.substitute(parameter.getType()))) {
return false;
}
}
} else {
if (myExpression.isExact()) {
final PsiMember applicableMember = myExpression.getPotentiallyApplicableMember();
LOG.assertTrue(applicableMember != null);
final PsiClass applicableMemberContainingClass = applicableMember.getContainingClass();
final PsiClass containingClass = qualifierResolveResult.getContainingClass();
@@ -90,7 +84,13 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
}
final PsiParameter[] parameters = applicableMember instanceof PsiMethod ? ((PsiMethod)applicableMember).getParameterList().getParameters() : PsiParameter.EMPTY_ARRAY;
if (targetParameters.length == parameters.length + 1) {
specialCase(session, constraints, substitutor, targetParameters, true);
final PsiType qualifierType = PsiMethodReferenceUtil.getQualifierType(myExpression);
final PsiClass qualifierClass = PsiUtil.resolveClassInType(qualifierType);
if (qualifierClass != null) {
session.initBounds(myExpression, qualifierClass.getTypeParameters());
final PsiType pType = substitutor.substitute(targetParameters[0].getType());
constraints.add(new StrictSubtypingConstraint(session.substituteWithInferenceVariables(qualifierType), pType));
}
for (int i = 1; i < targetParameters.length; i++) {
constraints.add(new TypeCompatibilityConstraint(session.substituteWithInferenceVariables(psiSubstitutor.substitute(parameters[i - 1].getType())),
substitutor.substitute(targetParameters[i].getType())));
@@ -123,6 +123,14 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
return true;
}
//------ non exact method references --------------------
for (PsiParameter parameter : targetParameters) {
if (!session.isProperType(substitutor.substitute(parameter.getType()))) {
return false;
}
}
final Map<PsiElement, PsiType> map = LambdaUtil.getFunctionalTypeMap();
final PsiType added = map.put(myExpression, session.startWithFreshVars(groundTargetType));
final JavaResolveResult resolve;
@@ -148,14 +156,28 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
final PsiType referencedMethodReturnType;
final PsiClass containingClass = method.getContainingClass();
LOG.assertTrue(containingClass != null, method);
PsiClass qContainingClass = qualifierResolveResult.getContainingClass();
final PsiClass qContainingClass = qualifierResolveResult.getContainingClass();
PsiSubstitutor psiSubstitutor = qualifierResolveResult.getSubstitutor();
if (qContainingClass != null) {
// 15.13.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(qContainingClass, psiSubstitutor)) {
psiSubstitutor = PsiSubstitutor.EMPTY;
if (targetParameters.length == method.getParameterList().getParametersCount() + 1) {
final PsiType pType = substitutor.substitute(targetParameters[0].getType());
PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(PsiUtil.captureToplevelWildcards(pType, myExpression));
PsiClass paramClass = resolveResult.getElement();
LOG.assertTrue(paramClass != null);
psiSubstitutor = TypeConversionUtil.getClassSubstitutor(qContainingClass, paramClass, resolveResult.getSubstitutor());
LOG.assertTrue(psiSubstitutor != null);
}
else {
psiSubstitutor = PsiSubstitutor.EMPTY;
}
}
if (qContainingClass.isInheritor(containingClass, true)) {
psiSubstitutor = TypeConversionUtil.getClassSubstitutor(containingClass, qContainingClass, PsiSubstitutor.EMPTY);
psiSubstitutor = TypeConversionUtil.getClassSubstitutor(containingClass, qContainingClass, psiSubstitutor);
LOG.assertTrue(psiSubstitutor != null);
}
}
@@ -202,52 +224,13 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm
}
}
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (targetParameters.length == parameters.length + 1 && !method.isVarArgs() &&
PsiPolyExpressionUtil.mentionsTypeParameters(referencedMethodReturnType, ContainerUtil.newHashSet(containingClass.getTypeParameters()))) { //todo specification bug?
specialCase(session, constraints, substitutor, targetParameters, false);
}
constraints.add(new TypeCompatibilityConstraint(returnType, session.substituteWithInferenceVariables(psiSubstitutor.substitute(referencedMethodReturnType))));
constraints.add(new TypeCompatibilityConstraint(returnType,
session.substituteWithInferenceVariables(psiSubstitutor.substitute(referencedMethodReturnType))));
}
return true;
}
private void specialCase(InferenceSession session,
List<ConstraintFormula> constraints,
PsiSubstitutor substitutor,
PsiParameter[] targetParameters,
boolean ignoreRaw) {
final PsiElement qualifier = myExpression.getQualifier();
PsiType qualifierType = null;
if (qualifier instanceof PsiTypeElement) {
qualifierType = ((PsiTypeElement)qualifier).getType();
final PsiClass qualifierClass = PsiUtil.resolveClassInType(qualifierType);
if (qualifierClass != null) {
qualifierType = JavaPsiFacade.getElementFactory(myExpression.getProject()).createType(qualifierClass, PsiSubstitutor.EMPTY);
}
}
else if (qualifier instanceof PsiExpression) {
qualifierType = ((PsiExpression)qualifier).getType();
if (qualifierType == null && qualifier instanceof PsiReferenceExpression) {
final JavaResolveResult resolveResult = ((PsiReferenceExpression)qualifier).advancedResolve(false);
final PsiElement res = resolveResult.getElement();
if (res instanceof PsiClass) {
PsiClass containingClass = (PsiClass)res;
final boolean isRawSubst = !ignoreRaw && !myExpression.isConstructor() && 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(myExpression, qualifierClass.getTypeParameters());
constraints.add(new StrictSubtypingConstraint(session.substituteWithInferenceVariables(qualifierType),
session.substituteWithInferenceVariables(substitutor.substitute(targetParameters[0].getType()))));
}
}
@Override
public void apply(PsiSubstitutor substitutor, boolean cache) {
myT = substitutor.substitute(myT);
@@ -0,0 +1,12 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Stream;
class Main {
public static void main(String[] args) {
Stream<List<? extends Main>> listStream = new ArrayList<List<? extends Main>>().stream();
Stream<? extends Main> l1 = listStream.flatMap(Collection::stream);
Stream<? extends Main> l2 = listStream.flatMap(List::stream);
}
}
@@ -342,6 +342,10 @@ public class NewMethodRefHighlightingTest extends LightDaemonAnalyzerTestCase {
doTest();
}
public void testRawReferenceTypeWithReceiver() throws Exception {
doTest();
}
private void doTest() {
doTest(false);
}