new inference: method refs: potentially compatible condition

This commit is contained in:
Anna Kozlova
2014-02-11 14:12:47 +01:00
parent dd66d26554
commit 9466bd9bde
5 changed files with 103 additions and 31 deletions

View File

@@ -38,6 +38,18 @@ public interface PsiMethodReferenceExpression extends PsiReferenceExpression {
boolean isExact();
/**
* 15.12.2.1 Identify Potentially Applicable Methods
* .................................................
* A method reference (15.13) is potentially compatible with a functional interface type if, where the type's function type arity is n,
* there exists at least one potentially-applicable method for the method reference at arity n (15.13.1), and one of the following is true:
* The method reference has the form ReferenceType::NonWildTypeArgumentsopt Identifier and at least one potentially-applicable method either
* i) is declared static and supports arity n, or
* ii) is not declared static and supports arity n-1.
* The method reference has some other form and at least one potentially-applicable method is not declared static.
*/
boolean isPotentiallyCompatible(PsiType functionalInterfaceType);
/**
* @return true if reference is of form ClassType::new
*/

View File

@@ -163,7 +163,12 @@ public class PsiMethodReferenceUtil {
if (methodReferenceExpression == null) return false;
final PsiElement argsList = PsiTreeUtil.getParentOfType(methodReferenceExpression, PsiExpressionList.class);
if (MethodCandidateInfo.ourOverloadGuard.currentStack().contains(argsList)) {
if (!methodReferenceExpression.isExact()) return true;
if (!methodReferenceExpression.isPotentiallyCompatible(left)) {
return false;
}
if (!methodReferenceExpression.isExact()) {
return true;
}
}
if (left instanceof PsiIntersectionType) {
for (PsiType conjunct : ((PsiIntersectionType)left).getConjuncts()) {

View File

@@ -104,26 +104,26 @@ public class MethodCandidateInfo extends CandidateInfo{
return getApplicabilityLevel();
}
final PsiMethod method = getElement();
if (method != null && method.hasTypeParameters() || myArgumentList == null || !PsiUtil.isLanguageLevel8OrHigher(myArgumentList)) {
@ApplicabilityLevelConstant int level;
if (myArgumentTypes == null) {
return ApplicabilityLevel.NOT_APPLICABLE;
}
else {
final PsiSubstitutor substitutor = getSubstitutor(false);
Integer boxedLevel = ourOverloadGuard.doPreventingRecursion(myArgumentList, false, new Computable<Integer>() {
@Override
public Integer compute() {
return PsiUtil.getApplicabilityLevel(getElement(), substitutor, myArgumentTypes, myLanguageLevel);
}
});
level = boxedLevel != null ? boxedLevel : getApplicabilityLevel();
}
if (level > ApplicabilityLevel.NOT_APPLICABLE && !isTypeArgumentsApplicable(false)) level = ApplicabilityLevel.NOT_APPLICABLE;
return level;
if (myArgumentTypes == null) {
return ApplicabilityLevel.NOT_APPLICABLE;
}
return getApplicabilityLevelInner();
@ApplicabilityLevelConstant int level;
Integer boxedLevel = ourOverloadGuard.doPreventingRecursion(myArgumentList, false, new Computable<Integer>() {
@Override
public Integer compute() {
final PsiMethod method = getElement();
if (method != null && method.hasTypeParameters() || myArgumentList == null || !PsiUtil.isLanguageLevel8OrHigher(myArgumentList)) {
return PsiUtil.getApplicabilityLevel(getElement(), getSubstitutor(false), myArgumentTypes, myLanguageLevel);
}
return getApplicabilityLevelInner();
}
});
level = boxedLevel != null ? boxedLevel : getApplicabilityLevel();
if (level > ApplicabilityLevel.NOT_APPLICABLE && !isTypeArgumentsApplicable(false)) level = ApplicabilityLevel.NOT_APPLICABLE;
return level;
}
public PsiSubstitutor getSiteSubstitutor() {

View File

@@ -37,6 +37,7 @@ import com.intellij.psi.scope.ElementClassFilter;
import com.intellij.psi.scope.JavaScopeProcessorEvent;
import com.intellij.psi.scope.PsiConflictResolver;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.scope.conflictResolvers.DuplicateConflictResolver;
import com.intellij.psi.scope.conflictResolvers.JavaMethodsConflictResolver;
import com.intellij.psi.scope.processor.FilterScopeProcessor;
import com.intellij.psi.scope.processor.MethodCandidatesProcessor;
@@ -74,6 +75,48 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
return getPotentiallyApplicableMember() != null;
}
@Override
public boolean isPotentiallyCompatible(final PsiType functionalInterfaceType) {
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
if (interfaceMethod == null) return false;
final MethodReferenceResolver resolver = new MethodReferenceResolver() {
@Override
protected PsiConflictResolver createResolver(PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult,
PsiMethod interfaceMethod,
MethodSignature signature) {
return DuplicateConflictResolver.INSTANCE;
}
@Override
protected PsiType getInterfaceType(PsiMethodReferenceExpression reference) {
return functionalInterfaceType;
}
};
final ResolveResult[] result = resolver.resolve(this, false);
final PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult = PsiMethodReferenceUtil.getQualifierResolveResult(this);
final int interfaceArity = interfaceMethod.getParameterList().getParametersCount();
for (ResolveResult resolveResult : result) {
final PsiElement element = resolveResult.getElement();
if (element instanceof PsiMethod) {
final boolean isStatic = ((PsiMethod)element).hasModifierProperty(PsiModifier.STATIC);
if (qualifierResolveResult.isReferenceTypeQualified() && getReferenceNameElement() instanceof PsiIdentifier) {
final int parametersCount = ((PsiMethod)element).getParameterList().getParametersCount();
if (parametersCount == interfaceArity && isStatic) {
return true;
}
if (parametersCount == interfaceArity - 1 && !isStatic) {
return true;
}
} else if (!isStatic) {
return true;
}
}
}
return false;
}
public PsiMember getPotentiallyApplicableMember() {
return CachedValuesManager.getCachedValue(this, new CachedValueProvider<PsiMember>() {
@Nullable
@@ -346,14 +389,7 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
if (isConstructor && (containingClass.isEnum() || containingClass.hasModifierProperty(PsiModifier.ABSTRACT))) {
return JavaResolveResult.EMPTY_ARRAY;
}
PsiType functionalInterfaceType = null;
final Map<PsiMethodReferenceExpression,PsiType> map = PsiMethodReferenceUtil.ourRefs.get();
if (map != null) {
functionalInterfaceType = FunctionalInterfaceParameterizationUtil.getGroundTargetType(map.get(reference));
}
if (functionalInterfaceType == null) {
functionalInterfaceType = getFunctionalInterfaceType();
}
final PsiType functionalInterfaceType = getInterfaceType(reference);
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
final PsiSubstitutor functionalInterfaceSubstitutor = interfaceMethod != null ? LambdaUtil.getSubstitutor(interfaceMethod, resolveResult) : null;
@@ -375,8 +411,7 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
return candidateInfo == null ? JavaResolveResult.EMPTY_ARRAY : new JavaResolveResult[]{candidateInfo};
}
final MethodReferenceConflictResolver conflictResolver =
new MethodReferenceConflictResolver(qualifierResolveResult, signature, interfaceMethod != null && interfaceMethod.isVarArgs());
final PsiConflictResolver conflictResolver = createResolver(qualifierResolveResult, interfaceMethod, signature);
final MethodCandidatesProcessor processor =
new MethodCandidatesProcessor(reference, getContainingFile(), new PsiConflictResolver[] {conflictResolver}, new SmartList<CandidateInfo>()) {
@Override
@@ -490,6 +525,26 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
return JavaResolveResult.EMPTY_ARRAY;
}
protected PsiType getInterfaceType(PsiMethodReferenceExpression reference) {
PsiType functionalInterfaceType = null;
final Map<PsiMethodReferenceExpression,PsiType> map = PsiMethodReferenceUtil.ourRefs.get();
if (map != null) {
functionalInterfaceType = FunctionalInterfaceParameterizationUtil.getGroundTargetType(map.get(reference));
}
if (functionalInterfaceType == null) {
functionalInterfaceType = reference.getFunctionalInterfaceType();
}
return functionalInterfaceType;
}
protected PsiConflictResolver createResolver(PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult,
PsiMethod interfaceMethod,
MethodSignature signature) {
return new MethodReferenceConflictResolver(qualifierResolveResult, signature, interfaceMethod != null && interfaceMethod.isVarArgs());
}
private PsiClassType composeReturnType(PsiClass containingClass, PsiSubstitutor substitutor) {
final boolean isRawSubst = PsiUtil.isRawSubstitutor(containingClass, substitutor);
return JavaPsiFacade.getElementFactory(containingClass.getProject())

View File

@@ -118,7 +118,7 @@ class MyTest2 {
call3(MyTest2::m1);
call3<error descr="Ambiguous method call: both 'MyTest2.call3(I1)' and 'MyTest2.call3(I2)' match">(MyTest2::m2)</error>;
call3(MyTest2::m3);
call3<error descr="Cannot resolve method 'call3(<method reference>)'">(MyTest2::m4)</error>;
call3<error descr="'call3(MyTest2.I2)' in 'MyTest2' cannot be applied to '(<method reference>)'">(MyTest2::m4)</error>;
}
}