method refs: filter more specific

This commit is contained in:
anna
2012-10-02 18:00:45 +02:00
parent 340ebdccdb
commit 9c796a4342
7 changed files with 189 additions and 16 deletions
@@ -639,7 +639,7 @@ public class LambdaUtil {
for (int i = 0; i < min; i++) {
final PsiType type1 = signatureParameterTypes1[offset + i];
final PsiType type2 = isVarargs && i == min - 1 ? ((PsiArrayType)signatureParameterTypes2[i]).getComponentType() : signatureParameterTypes2[i];
if (!GenericsUtil.eliminateWildcards(psiSubstitutor.substitute(type1)).equals(GenericsUtil.eliminateWildcards(type2))) {
if (!TypeConversionUtil.isAssignable(type2, psiSubstitutor.substitute(GenericsUtil.eliminateWildcards(type1)))) {
return false;
}
}
@@ -797,6 +797,21 @@ public class TypeConversionUtil {
}
public static boolean boxingConversionApplicable(final PsiType left, final PsiType right) {
if (right instanceof PsiMethodReferenceType) {
final JavaResolveResult result = ((PsiMethodReferenceType)right).getExpression().advancedResolve(false);
PsiElement element = result.getElement();
final PsiClassType.ClassResolveResult functionalInterfaceResult = PsiUtil.resolveGenericsClassInType(left);
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceResult);
if (element instanceof PsiMethod && interfaceMethod != null) {
final PsiType[] parameterTypes = ((PsiMethod)element).getSignature(result.getSubstitutor()).getParameterTypes();
final PsiType[] argTypes = interfaceMethod.getSignature(functionalInterfaceResult.getSubstitutor()).getParameterTypes();
if (parameterTypes.length != argTypes.length) return false;
for (int i = 0; i < parameterTypes.length; i++) {
if (boxingConversionApplicable(parameterTypes[i], argTypes[i])) return true;
}
}
}
if (left instanceof PsiPrimitiveType && !PsiType.NULL.equals(left)) {
return right instanceof PsiClassType && isAssignable(left, right);
}
@@ -21,6 +21,7 @@ import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiManagerEx;
import com.intellij.psi.impl.source.resolve.ParameterTypeInferencePolicy;
import com.intellij.psi.impl.source.resolve.ResolveCache;
import com.intellij.psi.impl.source.tree.ChildRole;
import com.intellij.psi.impl.source.tree.JavaElementType;
@@ -30,6 +31,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.JavaMethodsConflictResolver;
import com.intellij.psi.scope.processor.FilterScopeProcessor;
import com.intellij.psi.scope.processor.MethodCandidatesProcessor;
import com.intellij.psi.scope.util.PsiScopesUtil;
@@ -37,6 +39,7 @@ import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -242,10 +245,55 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
final MethodSignature signature = interfaceMethod != null ? interfaceMethod.getSignature(resolveResult.getSubstitutor()) : null;
final MethodReferenceConflictResolver conflictResolver = new MethodReferenceConflictResolver(containingClass, substitutor, signature, beginsWithReferenceType);
final MethodCandidatesProcessor processor = new MethodCandidatesProcessor(PsiMethodReferenceExpressionImpl.this,
new PsiConflictResolver[]{conflictResolver},
new SmartList<CandidateInfo>());
final MethodReferenceConflictResolver conflictResolver =
new MethodReferenceConflictResolver(containingClass, substitutor, signature, beginsWithReferenceType);
final PsiConflictResolver[] resolvers;
if (signature != null) {
final PsiType[] parameterTypes = signature.getParameterTypes();
resolvers = new PsiConflictResolver[]{conflictResolver,
new JavaMethodsConflictResolver(PsiMethodReferenceExpressionImpl.this, parameterTypes) {
@Override
public CandidateInfo resolveConflict(List<CandidateInfo> conflicts) {
boolean varargs = false;
for (CandidateInfo conflict : conflicts) {
final PsiElement psiElement = conflict.getElement();
if (psiElement instanceof PsiMethod && ((PsiMethod)psiElement).isVarArgs()) {
varargs = true;
break;
}
}
checkSpecifics(conflicts, varargs ? MethodCandidateInfo.ApplicabilityLevel.VARARGS : MethodCandidateInfo.ApplicabilityLevel.FIXED_ARITY);
return conflicts.size() == 1 ? conflicts.get(0) : null;
}
}};
}
else {
resolvers = new PsiConflictResolver[]{conflictResolver};
}
final MethodCandidatesProcessor processor =
new MethodCandidatesProcessor(PsiMethodReferenceExpressionImpl.this, resolvers, new SmartList<CandidateInfo>()) {
@Override
protected MethodCandidateInfo createCandidateInfo(final PsiMethod method,
PsiSubstitutor substitutor,
boolean staticProblem,
boolean accessible) {
final PsiExpressionList argumentList = getArgumentList();
return new MethodCandidateInfo(method, substitutor, !accessible, staticProblem, argumentList, myCurrentFileContext,
argumentList != null ? argumentList.getExpressionTypes() : null, getTypeArguments(),
getLanguageLevel()) {
@Override
public PsiSubstitutor inferTypeArguments(ParameterTypeInferencePolicy policy) {
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;
return JavaPsiFacade.getInstance(getProject()).getResolveHelper()
.inferTypeArguments(method.getTypeParameters(), types, rightTypes,
PsiUtil.getLanguageLevel(PsiMethodReferenceExpressionImpl.this));
}
};
}
};
processor.setIsConstructor(isConstructor);
processor.setName(isConstructor ? containingClass.getName() : element.getText());
@@ -323,7 +371,7 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
final PsiType type2 = varArgs && i >= signatureParameterTypes2.length - 1 ?
((PsiArrayType)signatureParameterTypes2[signatureParameterTypes2.length -1]).getComponentType() :
signatureParameterTypes2[i];
correct &= GenericsUtil.eliminateWildcards(subst.substitute(type1)).equals(GenericsUtil.eliminateWildcards(type2));
correct &= TypeConversionUtil.isAssignable(type2, subst.substitute(GenericsUtil.eliminateWildcards(type1)));
}
if (correct) {
firstCandidates.add(conflict);
@@ -335,7 +383,7 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
for (int i = 0; i < signatureParameterTypes2.length; i++) {
final PsiType type1 = parameterTypes[i + 1];
final PsiType type2 = signatureParameterTypes2[i];
correct &= GenericsUtil.eliminateWildcards(subst.substitute(type1)).equals(GenericsUtil.eliminateWildcards(type2));
correct &= TypeConversionUtil.isAssignable(type2, subst.substitute(GenericsUtil.eliminateWildcards(type1)));
}
if (correct) {
secondCandidates.add(conflict);
@@ -348,6 +396,8 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
if (acceptedCount == 0) {
conflicts.clear();
}
firstCandidates.addAll(secondCandidates);
conflicts.retainAll(firstCandidates);
return null;
}
return !firstCandidates.isEmpty() ? firstCandidates.get(0) : secondCandidates.get(0);
@@ -54,7 +54,7 @@ class MyTest2 {
static void call(Integer i, I s) { }
static void test() {
call<error descr="Cannot resolve method 'call(int, <method reference>)'">(1, MyTest2::m)</error>; //ambiguous
call<error descr="Ambiguous method call: both 'MyTest2.call(int, I)' and 'MyTest2.call(Integer, I)' match">(1, MyTest2::m)</error>; //ambiguous
}
}
@@ -0,0 +1,52 @@
class MyTest {
interface I1 {
void m(String s);
}
interface I2 {
void m(Integer s);
}
interface I3 {
void m(Object o);
}
static <T extends Number> void m(T p) {}
static <T1> void m1(T1 fx) { }
static void foo(I1 i) {}
static void foo(I2 i) {} //m
static void foo(I3 i) {}
static {
foo(MyTest::m);
foo<error descr="Ambiguous method call: both 'MyTest.foo(I2)' and 'MyTest.foo(I3)' match">(MyTest::m1)</error>;
}
}
class MyTest1 {
interface I1 {
void m(Integer s);
}
interface I2 {
void m(Integer s);
}
static <T extends Number> void m(T p) { }
static <T> void m1(T p) { }
static void foo1(I1 i) { }
static void foo2(I1 i) { }
static void foo2(I2 i) { }
static {
foo1(MyTest1::m);
foo2<error descr="Ambiguous method call: both 'MyTest1.foo2(I1)' and 'MyTest1.foo2(I2)' match">(MyTest1::m)</error>;
foo1(MyTest1::m1);
foo2<error descr="Ambiguous method call: both 'MyTest1.foo2(I1)' and 'MyTest1.foo2(I2)' match">(MyTest1::m1)</error>;
}
}
@@ -1,6 +1,5 @@
class MethodReference27 {
interface SAM {
class MyTest1 {
interface I {
void m(int i1, int i2);
}
@@ -13,10 +12,63 @@ class MethodReference27 {
static void m2(int... is) { }
static void m2(double... ds) {}
static void m3(int... is) { }
static void m3(Object... ds) {}
public static void main(String[] args) {
SAM s1 = MethodReference27::m1;
s1.m(42,42);
SAM s2 = MethodReference27 :: m2;
s2.m(42,42);
I i1 = MyTest1::m1;
i1.m(42,42);
I i2 = MyTest1 :: m2;
i2.m(42,42);
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest1.I'">I i3 = MyTest1 :: m3;</error>
}
}
class MyTest {
interface I1 {
void m(int i);
}
interface I2 {
void m(MyTest t, int i);
}
static void static_1(Integer i) {}
static void static_2(Integer i1, Integer i2) {}
static void static_3(String s) {}
static void static_4(String... ss) {}
void _1(Integer i) {}
void _2(Integer i1, Integer i2) {}
void _3(String s) {}
void _4(String... ss) {}
static {
I1 i1 = MyTest::static_1;
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest.I1'">I1 i2 = MyTest::static_2;</error>
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest.I1'">I1 i3 = MyTest::static_3;</error>
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest.I1'">I1 i4 = MyTest::static_4;</error>
}
{
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest.I1'">I1 i_1 = MyTest::_1;</error>
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest.I1'">I1 i_2 = MyTest::_2;</error>
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest.I1'">I1 i_3 = MyTest::_3;</error>
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest.I1'">I1 i_4 = MyTest::_4;</error>
I1 i1 = this::_1;
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest.I1'">I1 i2 = this::_2;</error>
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest.I1'">I1 i3 = this::_3;</error>
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest.I1'">I1 i4 = this::_4;</error>
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest.I2'">I2 i21 = MyTest::m1;</error>
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest.I2'">I2 i22 = MyTest::m2;</error>
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest.I2'">I2 i23 = MyTest::m3;</error>
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest.I2'">I2 i24 = MyTest::m4;</error>
}
}
@@ -40,7 +40,11 @@ public class MethodRefHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testMethodRefMisc() throws Exception {
doTest();
}
public void testMethodTypeParamsInference() throws Exception {
doTest();
}
public void testMethodRefMisc1() throws Exception {
doTest();
}