method refs: take qualifier's type arguments in account

This commit is contained in:
anna
2012-09-28 10:11:44 +02:00
parent 0e98677a71
commit 2cdfea8b61
4 changed files with 50 additions and 19 deletions
@@ -569,26 +569,34 @@ public class LambdaUtil {
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(left);
final PsiMethod method = getFunctionalInterfaceMethod(resolveResult);
if (method != null) {
final MethodSignature signature1 = method.getSignature(resolveResult.getSubstitutor());
final MethodSignature signature2 = ((PsiMethod)resolve).getSignature(JavaPsiFacade.getElementFactory(method.getProject()).createRawSubstitutor(
(PsiTypeParameterListOwner)resolve));
final Ref<PsiClass> classRef = new Ref<PsiClass>();
methodReferenceExpression.process(classRef, new Ref<PsiSubstitutor>());
if (areAcceptable(signature1, signature2, classRef.get())) return true;
final Ref<PsiSubstitutor> substRef = new Ref<PsiSubstitutor>();
methodReferenceExpression.process(classRef, substRef);
final MethodSignature signature1 = method.getSignature(resolveResult.getSubstitutor());
final MethodSignature signature2 = ((PsiMethod)resolve).getSignature(substRef.get());
if (areAcceptable(signature1, signature2, classRef.get(), substRef.get())) return true;
}
}
return false;
}
public static boolean areAcceptable(MethodSignature signature1, MethodSignature signature2, PsiClass psiClass) {
public static boolean areAcceptable(MethodSignature signature1,
MethodSignature signature2,
PsiClass psiClass,
PsiSubstitutor psiSubstitutor) {
int offset = 0;
final PsiType[] signatureParameterTypes1 = signature1.getParameterTypes();
final PsiType[] signatureParameterTypes2 = signature2.getParameterTypes();
if (signatureParameterTypes1.length != signatureParameterTypes2.length) {
if (signatureParameterTypes1.length == signatureParameterTypes2.length + 1 &&
//todo correct check needed
PsiUtil.resolveClassInType(TypeConversionUtil.erasure(signatureParameterTypes1[0], signature1.getSubstitutor())) == psiClass) {
offset++;
if (signatureParameterTypes1.length == signatureParameterTypes2.length + 1) {
final PsiClassType classType = JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass, psiSubstitutor);
if (signatureParameterTypes1[0].equals(classType)) {
offset++;
}
else {
return false;
}
}
else {
return false;
@@ -33,6 +33,7 @@ import com.intellij.psi.scope.processor.FilterScopeProcessor;
import com.intellij.psi.scope.processor.MethodCandidatesProcessor;
import com.intellij.psi.scope.util.PsiScopesUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
@@ -216,7 +217,7 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
final PsiType functionalInterfaceType = getFunctionalInterfaceType();
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
final MethodReferenceConflictResolver conflictResolver = new MethodReferenceConflictResolver(containingClass, interfaceMethod, resolveResult.getSubstitutor());
final MethodReferenceConflictResolver conflictResolver = new MethodReferenceConflictResolver(containingClass, substitutor, interfaceMethod != null ? interfaceMethod.getSignature(resolveResult.getSubstitutor()) : null);
final MethodCandidatesProcessor processor = new MethodCandidatesProcessor(PsiMethodReferenceExpressionImpl.this,
new PsiConflictResolver[]{conflictResolver}, new SmartList<CandidateInfo>());
processor.setIsConstructor(false);
@@ -234,27 +235,29 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
private class MethodReferenceConflictResolver implements PsiConflictResolver {
private final PsiClass myContainingClass;
private PsiMethod myFunctionalInterface;
private final PsiSubstitutor mySubstitutor;
private MethodReferenceConflictResolver(PsiClass containingClass, @Nullable PsiMethod psiMethod, PsiSubstitutor substitutor) {
private final MethodSignature mySignature;
private MethodReferenceConflictResolver(PsiClass containingClass,
PsiSubstitutor psiSubstitutor,
@Nullable MethodSignature signature) {
myContainingClass = containingClass;
myFunctionalInterface = psiMethod;
mySubstitutor = substitutor;
mySubstitutor = psiSubstitutor;
mySignature = signature;
}
@Nullable
@Override
public CandidateInfo resolveConflict(List<CandidateInfo> conflicts) {
if (myFunctionalInterface == null) return null;
if (mySignature == null) return null;
for (Iterator<CandidateInfo> iterator = conflicts.iterator(); iterator.hasNext(); ) {
CandidateInfo conflict = iterator.next();
if (!(conflict instanceof MethodCandidateInfo)) continue;
final PsiMethod psiMethod = ((MethodCandidateInfo)conflict).getElement();
if (psiMethod == null) continue;
if (!LambdaUtil.areAcceptable(myFunctionalInterface.getSignature(mySubstitutor),
psiMethod.getSignature(conflict.getSubstitutor()), myContainingClass)) {
if (!LambdaUtil.areAcceptable(mySignature,
psiMethod.getSignature(conflict.getSubstitutor()), myContainingClass, mySubstitutor)) {
iterator.remove();
}
}
@@ -0,0 +1,16 @@
class MyTest {
interface I {
String m(Foo<String> f);
}
class Foo<X> {
String foo() {
return null;
}
{
I i = Foo<String> :: foo;
<error descr="Incompatible types. Found: '<method reference>', required: 'MyTest.I'">I i1 = Foo<Integer> :: foo;</error>
}
}
}
@@ -41,6 +41,10 @@ public class MethodRefHighlightingTest extends LightDaemonAnalyzerTestCase {
doTest();
}
public void testQualifierTypeArgs() throws Exception {
doTest();
}
private void doTest() throws Exception {
doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false);
}