mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 13:02:30 +07:00
new inference: method refs: potentially compatible condition
This commit is contained in:
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user