method refs: choose more specific return type when checking more specific functional interface

This commit is contained in:
anna
2012-10-02 21:15:27 +02:00
parent 65983d0060
commit ff500e1468
5 changed files with 76 additions and 3 deletions
@@ -16,9 +16,11 @@
package com.intellij.psi;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.*;
import org.jetbrains.annotations.NotNull;
@@ -590,9 +592,14 @@ public class LambdaUtil {
final MethodSignature signature1 = method.getSignature(resolveResult.getSubstitutor());
PsiSubstitutor subst = PsiSubstitutor.EMPTY;
subst = subst.putAll(result.getSubstitutor());
subst = subst.putAll(substRef.get());
subst = subst.putAll(result.getSubstitutor());
final MethodSignature signature2 = ((PsiMethod)resolve).getSignature(subst);
final PsiType interfaceReturnType = getFunctionalInterfaceReturnType(left);
final PsiType methodReturnType = subst.substitute(((PsiMethod)resolve).getReturnType());
if (interfaceReturnType != null && methodReturnType != null && interfaceReturnType != PsiType.VOID &&
!TypeConversionUtil.isAssignable(interfaceReturnType, methodReturnType)) return false;
if (areAcceptable(signature1, signature2, classRef.get(), substRef.get(), ((PsiMethod)resolve).isVarArgs())) return true;
}
}
@@ -662,6 +669,35 @@ public class LambdaUtil {
return (PsiLocalVariable)((PsiDeclarationStatement)assignmentFromText).getDeclaredElements()[0];
}
public static void processMethodReferenceReturnType(List<CandidateInfo> conflicts, int functionalInterfaceIdx) {
final CandidateInfo[] newConflictsArray = conflicts.toArray(new CandidateInfo[conflicts.size()]);
for (int i = 1; i < newConflictsArray.length; i++) {
final CandidateInfo method = newConflictsArray[i];
final PsiType interfaceReturnType = getReturnType(functionalInterfaceIdx, method);
for (int j = 0; j < i; j++) {
final CandidateInfo conflict = newConflictsArray[j];
assert conflict != method;
final PsiType interfaceReturnType1 = getReturnType(functionalInterfaceIdx, conflict);
if (interfaceReturnType != null && interfaceReturnType1 != null && !Comparing.equal(interfaceReturnType, interfaceReturnType1)) {
if (TypeConversionUtil.isAssignable(interfaceReturnType, interfaceReturnType1)) {
conflicts.remove(method);
break;
} else if (TypeConversionUtil.isAssignable(interfaceReturnType1, interfaceReturnType)) {
conflicts.remove(conflict);
}
}
}
}
}
@Nullable
private static PsiType getReturnType(int functionalTypeIdx, CandidateInfo method) {
final PsiParameter[] methodParameters = ((PsiMethod)method.getElement()).getParameterList().getParameters();
final PsiParameter param = functionalTypeIdx < methodParameters.length ? methodParameters[functionalTypeIdx] : methodParameters[methodParameters.length - 1];
final PsiType functionalInterfaceType = method.getSubstitutor().substitute(param.getType());
return getFunctionalInterfaceReturnType(functionalInterfaceType);
}
private static class TypeParamsChecker extends PsiTypeVisitor<Boolean> {
private PsiMethod myMethod;
private final PsiClass myClass;
@@ -288,7 +288,11 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
if (signature == null) return PsiSubstitutor.EMPTY;
final PsiType[] types = method.getSignature(PsiSubstitutor.EMPTY).getParameterTypes();
final PsiType[] rightTypes = signature.getParameterTypes();
if (types.length != rightTypes.length) return PsiSubstitutor.EMPTY;
if (types.length < rightTypes.length) {
return PsiUtil.resolveGenericsClassInType(rightTypes[0]).getSubstitutor();
} else if (types.length > rightTypes.length) {
return PsiUtil.resolveGenericsClassInType(types[0]).getSubstitutor();
}
return JavaPsiFacade.getInstance(getProject()).getResolveHelper()
.inferTypeArguments(method.getTypeParameters(), types, rightTypes,
PsiUtil.getLanguageLevel(PsiMethodReferenceExpressionImpl.this));
@@ -356,8 +360,8 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
final PsiMethod psiMethod = ((MethodCandidateInfo)conflict).getElement();
if (psiMethod == null) continue;
PsiSubstitutor subst = PsiSubstitutor.EMPTY;
subst = subst.putAll(conflict.getSubstitutor());
subst = subst.putAll(mySubstitutor);
subst = subst.putAll(conflict.getSubstitutor());
final PsiType[] signatureParameterTypes2 = psiMethod.getSignature(subst).getParameterTypes();
final boolean varArgs = psiMethod.isVarArgs();
@@ -125,6 +125,9 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
}
}
}
else if (parameterType instanceof PsiMethodReferenceType) {
LambdaUtil.processMethodReferenceReturnType(conflicts, i);
}
}
}
@@ -0,0 +1,26 @@
class MyTest {
static class Foo<T> {
T m() { return null; };
}
interface I1 {
Foo<Object> m(Foo<String> f);
}
interface I2 {
Integer m(Foo<Integer> f);
}
interface I3 {
Object m(Foo<Integer> f);
}
static void foo(I1 i) {}
static void foo(I2 i) {}
static void foo(I3 i) {}
static {
foo(Foo::m);
}
}
@@ -81,6 +81,10 @@ public class MethodRefHighlightingTest extends LightDaemonAnalyzerTestCase {
doTest();
}
public void testReturnTypeSpecific() throws Exception {
doTest();
}
private void doTest() throws Exception {
doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false);
}