new inference: overload resolution: check SAM specific withing common specific check

(cherry picked from commit 2bbd45e57bc5df7ff2a5cae7acedc0d2428d9afe)
This commit is contained in:
anna
2013-11-25 16:47:58 +01:00
parent e3197b6581
commit 82fd892ec5
3 changed files with 111 additions and 91 deletions
@@ -124,24 +124,11 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
final PsiType paramType = param.getType();
if (!LambdaUtil.isAcceptable(lambdaExpression, conflict.getSubstitutor().substitute(paramType), lambdaExpression.hasFormalParameterTypes())) {
iterator.remove();
} else {
/*todo
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(paramType);
final PsiMethod functionalInterfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(paramType);
if (functionalInterfaceMethod != null) {
for (PsiParameter parameter : functionalInterfaceMethod.getParameterList().getParameters()) {
if (LambdaUtil.dependsOnTypeParams(resolveResult.getSubstitutor().substitute(parameter.getType()), resolveResult.getElement(), method)) {
iterator.remove();
break;
}
}
}*/
}
}
}
}
}
checkMoreSpecificReturnType(conflicts, getActualParameterTypes(), languageLevel);
}
public void checkSpecifics(@NotNull List<CandidateInfo> conflicts,
@@ -522,11 +509,8 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
final PsiType[] types2AtSite = typesAtSite(types2, siteSubstitutor2);
final PsiType[] types1AtSite = typesAtSite(types1, siteSubstitutor1);
final PsiSubstitutor methodSubstitutor1 = calculateMethodSubstitutor(typeParameters1, method1, siteSubstitutor1, types1, types2AtSite, languageLevel);
final PsiSubstitutor methodSubstitutor2 = calculateMethodSubstitutor(typeParameters2, method2, siteSubstitutor2, types2, types1AtSite, languageLevel);
final boolean applicable12 = isApplicableTo(types2AtSite, method1, typeParameters1, languageLevel, methodSubstitutor1, varargsPosition);
final boolean applicable21 = isApplicableTo(types1AtSite, method2, typeParameters2, languageLevel, methodSubstitutor2, varargsPosition);
final boolean applicable12 = isApplicableTo(types2AtSite, method1, typeParameters1, languageLevel, varargsPosition, types1, siteSubstitutor1);
final boolean applicable21 = isApplicableTo(types1AtSite, method2, typeParameters2, languageLevel, varargsPosition, types2, siteSubstitutor2);
if (applicable12 || applicable21) {
@@ -542,6 +526,53 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
return Specifics.FIRST;
}
}
if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
boolean toCompareFunctional = false;
for (int i = 0; i < myActualParameterTypes.length; i++) {
if (types1.length > 0 && LambdaHighlightingUtil.checkInterfaceFunctional(types1[Math.min(i, types1.length - 1)]) == null &&
types2.length > 0 && LambdaHighlightingUtil.checkInterfaceFunctional(types2[Math.min(i, types2.length - 1)]) == null) {
types1AtSite[Math.min(i, types1.length - 1)] = PsiType.NULL;
types2AtSite[Math.min(i, types2.length - 1)] = PsiType.NULL;
toCompareFunctional = true;
}
}
if (toCompareFunctional) {
final boolean applicable12ignoreFunctionalType = isApplicableTo(types2AtSite, method1, typeParameters1, languageLevel, varargsPosition, types1, siteSubstitutor1);
final boolean applicable21ignoreFunctionalType = isApplicableTo(types1AtSite, method2, typeParameters2, languageLevel, varargsPosition, types2, siteSubstitutor2);
if (applicable12ignoreFunctionalType || applicable21ignoreFunctionalType) {
Specifics specifics = null;
for (int i = 0; i < myActualParameterTypes.length; i++) {
if (types1.length > 0 && types1AtSite[Math.min(i, types1.length - 1)] == PsiType.NULL &&
types2.length > 0 && types2AtSite[Math.min(i, types2.length - 1)] == PsiType.NULL) {
Specifics specific = compareFunctionalTypes(info1, info2, myActualParameterTypes[i], i, languageLevel);
if (specific == Specifics.NEITHER) {
specifics = Specifics.NEITHER;
break;
}
if (specifics == null) {
specifics = specific;
} else if (specifics != specific) {
specifics = Specifics.NEITHER;
break;
}
}
}
if (applicable12ignoreFunctionalType && !applicable21ignoreFunctionalType) {
return specifics == Specifics.SECOND ? Specifics.SECOND : Specifics.NEITHER;
}
if (applicable21ignoreFunctionalType && !applicable12ignoreFunctionalType) {
return specifics == Specifics.FIRST ? Specifics.FIRST : Specifics.NEITHER;
}
return specifics;
}
}
}
}
else if (varargsPosition) {
final PsiType lastParamType1 = classSubstitutor1.substitute(types1[types1.length - 1]);
@@ -585,13 +616,15 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
return Specifics.NEITHER;
}
private boolean isApplicableTo(PsiType[] argTypes,
PsiMethod method,
PsiTypeParameter[] typeParameters,
LanguageLevel languageLevel, PsiSubstitutor methodSubstitutor, boolean checkVarargs) {
final int applicabilityLevel = PsiUtil.getApplicabilityLevel(method, methodSubstitutor, argTypes, languageLevel, false, checkVarargs);
private boolean isApplicableTo(PsiType[] types2AtSite,
PsiMethod method1,
PsiTypeParameter[] typeParameters1,
LanguageLevel languageLevel,
boolean varargsPosition, PsiType[] types1, PsiSubstitutor siteSubstitutor1) {
final PsiSubstitutor methodSubstitutor1 = calculateMethodSubstitutor(typeParameters1, method1, siteSubstitutor1, types1, types2AtSite, languageLevel);
final int applicabilityLevel = PsiUtil.getApplicabilityLevel(method1, methodSubstitutor1, types2AtSite, languageLevel, false, varargsPosition);
final boolean applicable = applicabilityLevel > MethodCandidateInfo.ApplicabilityLevel.NOT_APPLICABLE;
if (applicable && !GenericsUtil.isTypeArgumentsApplicable(typeParameters, methodSubstitutor, myArgumentsList, false)) {
if (applicable && !GenericsUtil.isTypeArgumentsApplicable(typeParameters1, methodSubstitutor1, myArgumentsList, false)) {
return false;
}
return applicable;
@@ -662,77 +695,40 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
PRIMITIVE, REFERENCE, NONE_DETERMINED
}
public void checkMoreSpecificReturnType(List<CandidateInfo> conflicts, PsiType[] actualParameterTypes, LanguageLevel languageLevel) {
final CandidateInfo[] newConflictsArray = conflicts.toArray(new CandidateInfo[conflicts.size()]);
next:
for (int i = 1; i < newConflictsArray.length; i++) {
final CandidateInfo method = newConflictsArray[i];
for (int j = 0; j < i; j++) {
final CandidateInfo conflict = newConflictsArray[j];
assert conflict != method;
switch (isMoreSpecific(method, conflict, actualParameterTypes, languageLevel)) {
case FIRST:
conflicts.remove(conflict);
break;
case SECOND:
conflicts.remove(method);
continue next;
default:
break;
}
}
}
}
private static Specifics isMoreSpecific(CandidateInfo method,
CandidateInfo conflict,
PsiType[] actualParameterTypes,
LanguageLevel languageLevel) {
Specifics moreSpecific = Specifics.NEITHER;
private static Specifics compareFunctionalTypes(CandidateInfo method,
CandidateInfo conflict,
PsiType paramType,
int functionalInterfaceIdx,
LanguageLevel languageLevel) {
final PsiMethod methodElement = (PsiMethod)method.getElement();
final PsiMethod conflictElement = (PsiMethod)conflict.getElement();
if (methodElement != null &&
conflictElement != null &&
methodElement.isVarArgs() == conflictElement.isVarArgs() &&
methodElement.getParameterList().getParametersCount() <= actualParameterTypes.length &&
conflictElement.getParameterList().getParametersCount() <= actualParameterTypes.length) {
for (int functionalInterfaceIdx = 0; functionalInterfaceIdx < actualParameterTypes.length; functionalInterfaceIdx++) {
final PsiType interfaceReturnType = getReturnType(functionalInterfaceIdx, method);
final PsiType interfaceReturnType1 = getReturnType(functionalInterfaceIdx, conflict);
if (actualParameterTypes[functionalInterfaceIdx] instanceof PsiLambdaExpressionType) {
final PsiLambdaExpression lambdaExpression = ((PsiLambdaExpressionType)actualParameterTypes[functionalInterfaceIdx]).getExpression();
if (!lambdaExpression.hasFormalParameterTypes()) {
return Specifics.NEITHER;
}
}
if (actualParameterTypes[functionalInterfaceIdx] instanceof PsiMethodReferenceType) {
final PsiMethodReferenceExpression
methodReferenceExpression = ((PsiMethodReferenceType)actualParameterTypes[functionalInterfaceIdx]).getExpression();
if (!methodReferenceExpression.isExact()) {
return Specifics.NEITHER;
}
}
if (actualParameterTypes[functionalInterfaceIdx] instanceof PsiLambdaExpressionType || actualParameterTypes[functionalInterfaceIdx] instanceof PsiMethodReferenceType) {
if (interfaceReturnType != null && interfaceReturnType1 != null && !Comparing.equal(interfaceReturnType, interfaceReturnType1)) {
Specifics moreSpecific1 = comparePrimitives(actualParameterTypes[functionalInterfaceIdx], interfaceReturnType, interfaceReturnType1);
if (moreSpecific1 == Specifics.NEITHER && (interfaceReturnType != PsiType.VOID && interfaceReturnType1 != PsiType.VOID)) {
moreSpecific1 = compareConflicts((MethodCandidateInfo)method, (MethodCandidateInfo)conflict,
methodElement, conflictElement,
interfaceReturnType, interfaceReturnType1, languageLevel);
}
if (moreSpecific != Specifics.NEITHER && moreSpecific != moreSpecific1) {
return Specifics.NEITHER;
}
moreSpecific = moreSpecific1;
}
} else if (interfaceReturnType != null && interfaceReturnType1 != null) {
return Specifics.NEITHER;
}
final PsiType interfaceReturnType = getReturnType(functionalInterfaceIdx, method);
final PsiType interfaceReturnType1 = getReturnType(functionalInterfaceIdx, conflict);
if (paramType instanceof PsiLambdaExpressionType) {
final PsiLambdaExpression lambdaExpression = ((PsiLambdaExpressionType)paramType).getExpression();
if (!lambdaExpression.hasFormalParameterTypes()) {
return Specifics.NEITHER;
}
}
return moreSpecific;
if (paramType instanceof PsiMethodReferenceType) {
final PsiMethodReferenceExpression
methodReferenceExpression = ((PsiMethodReferenceType)paramType).getExpression();
if (!methodReferenceExpression.isExact()) {
return Specifics.NEITHER;
}
}
if (paramType instanceof PsiLambdaExpressionType || paramType instanceof PsiMethodReferenceType) {
if (interfaceReturnType != null && interfaceReturnType1 != null && !Comparing.equal(interfaceReturnType, interfaceReturnType1)) {
Specifics moreSpecific1 = comparePrimitives(paramType, interfaceReturnType, interfaceReturnType1);
if (moreSpecific1 == Specifics.NEITHER && (interfaceReturnType != PsiType.VOID && interfaceReturnType1 != PsiType.VOID)) {
return compareConflicts((MethodCandidateInfo)method, (MethodCandidateInfo)conflict,
methodElement, conflictElement,
interfaceReturnType, interfaceReturnType1, languageLevel);
}
return moreSpecific1;
}
}
return Specifics.NEITHER;
}
private static Specifics compareConflicts(MethodCandidateInfo method,
@@ -0,0 +1,20 @@
class Test {
interface IInt {
int _();
}
interface ILong {
long _();
}
void m(IInt i, Long l) {}
void m(ILong l, Integer i) {}
void m1(IInt i, Integer l) {}
void m1(ILong l, Object i) {}
void test() {
m<error descr="Ambiguous method call: both 'Test.m(IInt, Long)' and 'Test.m(ILong, Integer)' match">(() -> 1, null)</error>;
m1(() -> 1, null);
}
}
@@ -66,6 +66,10 @@ public class NewLambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
doTest();
}
public void testOverloadResolutionSAM() throws Exception {
doTest();
}
private void doTest() {
doTest(false);
}