new inference: method ref inference: postpone return type inference in case of constructor refs

This commit is contained in:
Anna Kozlova
2014-02-07 18:12:06 +01:00
parent 5fb1dbb2ce
commit 6efef684ce
3 changed files with 28 additions and 24 deletions

View File

@@ -17,7 +17,6 @@ package com.intellij.psi.impl.source.tree.java;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange;
import com.intellij.pom.java.LanguageLevel;
@@ -355,26 +354,20 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
final MethodSignature signature = interfaceMethod != null ? interfaceMethod.getSignature(LambdaUtil.getSubstitutor(interfaceMethod, resolveResult)) : null;
final PsiType interfaceMethodReturnType = LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType);
if (isConstructor && interfaceMethod != null) {
final PsiTypeParameter[] typeParameters = containingClass.getTypeParameters();
final boolean isRawSubst = PsiUtil.isRawSubstitutor(containingClass, substitutor);
final PsiClassType returnType = JavaPsiFacade.getElementFactory(containingClass.getProject())
.createType(containingClass, isRawSubst ? PsiSubstitutor.EMPTY : substitutor);
final InferenceSession session = new InferenceSession(typeParameters, substitutor, getManager(), null);
if (isConstructor && interfaceMethod != null && containingClass.getConstructors().length == 0) {
final PsiClassType returnType = composeReturnType(containingClass, substitutor);
final InferenceSession session = new InferenceSession(containingClass.getTypeParameters(), substitutor, getManager(), null);
if (!(session.isProperType(returnType) && session.isProperType(interfaceMethodReturnType))) {
session.registerConstraints(returnType, interfaceMethodReturnType);
substitutor = session.infer();
}
if (containingClass.getConstructors().length == 0) {
ClassCandidateInfo candidateInfo = null;
if ((containingClass.getContainingClass() == null || !isLocatedInStaticContext(containingClass)) && signature.getParameterTypes().length == 0 ||
PsiMethodReferenceUtil.onArrayType(containingClass, signature)) {
candidateInfo = new ClassCandidateInfo(containingClass, substitutor);
}
return candidateInfo == null ? JavaResolveResult.EMPTY_ARRAY : new JavaResolveResult[]{candidateInfo};
ClassCandidateInfo candidateInfo = null;
if ((containingClass.getContainingClass() == null || !isLocatedInStaticContext(containingClass)) &&
signature.getParameterTypes().length == 0 ||
PsiMethodReferenceUtil.onArrayType(containingClass, signature)) {
candidateInfo = new ClassCandidateInfo(containingClass, substitutor);
}
return candidateInfo == null ? JavaResolveResult.EMPTY_ARRAY : new JavaResolveResult[]{candidateInfo};
}
final MethodReferenceConflictResolver conflictResolver =
@@ -403,10 +396,16 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
@Override
public PsiSubstitutor inferTypeArguments(@NotNull ParameterTypeInferencePolicy policy, boolean includeReturnConstraint) {
if (interfaceMethod == null) return substitutor;
final PsiSubstitutor qualifierResultSubstitutor = qualifierResolveResult.getSubstitutor();
final InferenceSession session = new InferenceSession(method.getTypeParameters(), substitutor, getManager(), PsiMethodReferenceExpressionImpl.this);
final PsiParameter[] functionalMethodParameters = interfaceMethod.getParameterList().getParameters();
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (parameters.length == functionalMethodParameters.length) {//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(parameters[i].getType(), pType));
@@ -415,7 +414,6 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
else if (parameters.length + 1 == functionalMethodParameters.length) { //instance methods
final PsiClass aClass = qualifierResolveResult.getContainingClass();
session.initBounds(aClass.getTypeParameters());
final PsiSubstitutor qualifierResultSubstitutor = qualifierResolveResult.getSubstitutor();
final PsiType pType = signature.getParameterTypes()[0];
@@ -423,16 +421,16 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
// 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(aClass, qualifierResultSubstitutor)) {
if (PsiUtil.isRawSubstitutor(containingClass, qualifierResultSubstitutor)) {
final PsiClassType.ClassResolveResult pResult = PsiUtil.resolveGenericsClassInType(pType);
final PsiClass pClass = pResult.getElement();
final PsiSubstitutor receiverSubstitutor = pClass != null ? TypeConversionUtil.getClassSubstitutor(aClass, pClass, pResult.getSubstitutor()) : null;
final PsiSubstitutor receiverSubstitutor = pClass != null ? TypeConversionUtil.getClassSubstitutor(containingClass, pClass, pResult.getSubstitutor()) : null;
if (receiverSubstitutor != null) {
psiSubstitutor = receiverSubstitutor;
}
}
final PsiType qType = JavaPsiFacade.getElementFactory(getProject()).createType(aClass, psiSubstitutor);
final PsiType qType = JavaPsiFacade.getElementFactory(getProject()).createType(containingClass, psiSubstitutor);
session.addConstraint(new TypeCompatibilityConstraint(qType, pType));
@@ -447,7 +445,7 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
boolean success = session.repeatInferencePhases(false);
final PsiType returnType = method.getReturnType();
final PsiType returnType = method.isConstructor() ? composeReturnType(containingClass, substitutor) : method.getReturnType();
if (returnType != null) {
session.registerConstraints(returnType, interfaceMethodReturnType);
}
@@ -476,6 +474,12 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
return JavaResolveResult.EMPTY_ARRAY;
}
private PsiClassType composeReturnType(PsiClass containingClass, PsiSubstitutor substitutor) {
final boolean isRawSubst = PsiUtil.isRawSubstitutor(containingClass, substitutor);
return JavaPsiFacade.getElementFactory(containingClass.getProject())
.createType(containingClass, isRawSubst ? PsiSubstitutor.EMPTY : substitutor);
}
private class MethodReferenceConflictResolver implements PsiConflictResolver {
private final MethodSignature mySignature;
private final PsiMethodReferenceUtil.QualifierResolveResult myQualifierResolveResult;

View File

@@ -39,6 +39,6 @@ class MyTest1 {
static void foo(I3 i) {}
static {
foo<error descr="Cannot resolve method 'foo(<method reference>)'">(Foo::new)</error>;
foo<error descr="Ambiguous method call: both 'MyTest1.foo(I1)' and 'MyTest1.foo(I2)' match">(Foo::new)</error>;
}
}

View File

@@ -67,7 +67,7 @@ class MyTest1 {
}
public static void main(String[] args) {
m<error descr="Ambiguous method call: both 'MyTest1.m(I2)' and 'MyTest1.m(I3)' match">(Foo::new)</error>;
m<error descr="Ambiguous method call: both 'MyTest1.m(I1)' and 'MyTest1.m(I2)' match">(Foo::new)</error>;
}
}
class MyTest2 {
@@ -104,6 +104,6 @@ class MyTest2 {
}
public static void main(String[] args) {
m<error descr="Ambiguous method call: both 'MyTest2.m(I2)' and 'MyTest2.m(I3)' match">(Foo::new)</error>;
m<error descr="Ambiguous method call: both 'MyTest2.m(I1)' and 'MyTest2.m(I2)' match">(Foo::new)</error>;
}
}