method refs: ambiguity checks into resolver; testdata

This commit is contained in:
anna
2012-09-27 17:59:03 +02:00
parent 67d6396df2
commit bcebb55f60
8 changed files with 180 additions and 49 deletions

View File

@@ -17,6 +17,7 @@ package com.intellij.psi;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.*;
@@ -571,20 +572,33 @@ public class LambdaUtil {
final MethodSignature signature1 = method.getSignature(resolveResult.getSubstitutor());
final MethodSignature signature2 = ((PsiMethod)resolve).getSignature(JavaPsiFacade.getElementFactory(method.getProject()).createRawSubstitutor(
(PsiTypeParameterListOwner)resolve));
if (areAcceptable(signature1, signature2)) return true;
final Ref<PsiClass> classRef = new Ref<PsiClass>();
methodReferenceExpression.process(classRef, new Ref<PsiSubstitutor>());
if (areAcceptable(signature1, signature2, classRef.get())) return true;
}
}
return false;
}
public static boolean areAcceptable(MethodSignature signature1, MethodSignature signature2) {
public static boolean areAcceptable(MethodSignature signature1, MethodSignature signature2, PsiClass psiClass) {
int offset = 0;
final PsiType[] signatureParameterTypes1 = signature1.getParameterTypes();
final PsiType[] signatureParameterTypes2 = signature2.getParameterTypes();
if (signatureParameterTypes1.length != signatureParameterTypes2.length) return false;
for (int i = 0; i < signatureParameterTypes1.length; i++) {
final PsiType type1 = signatureParameterTypes1[i];
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++;
}
else {
return false;
}
}
for (int i = 0; i < signatureParameterTypes2.length; i++) {
final PsiType type1 = signatureParameterTypes1[offset + i];
final PsiType type2 = signatureParameterTypes2[i];
if (!TypeConversionUtil.isAssignable(GenericsUtil.eliminateWildcards(type2), GenericsUtil.eliminateWildcards(type1))) {
if (!GenericsUtil.eliminateWildcards(type1).equals(GenericsUtil.eliminateWildcards(type2))) {
return false;
}
}

View File

@@ -15,6 +15,7 @@
*/
package com.intellij.psi;
import com.intellij.openapi.util.Ref;
import org.jetbrains.annotations.Nullable;
/**
@@ -31,4 +32,6 @@ public interface PsiMethodReferenceExpression extends PsiReferenceExpression {
@Nullable
PsiType getFunctionalInterfaceType();
void process(Ref<PsiClass> psiClassRef, Ref<PsiSubstitutor> substRef);
}

View File

@@ -17,6 +17,7 @@ package com.intellij.psi.impl.source.tree.java;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiManagerEx;
@@ -24,20 +25,22 @@ import com.intellij.psi.impl.source.resolve.ResolveCache;
import com.intellij.psi.impl.source.tree.ChildRole;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.scope.ElementClassFilter;
import com.intellij.psi.scope.PsiConflictResolver;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.scope.conflictResolvers.DuplicateConflictResolver;
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;
import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import java.util.List;
public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase implements PsiMethodReferenceExpression {
private static Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.PsiMethodReferenceExpressionImpl");
@@ -164,56 +167,58 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
return "PsiMethodReferenceExpression:" + getText();
}
private class MethodReferenceResolver implements ResolveCache.PolyVariantResolver<PsiJavaReference> {
@NotNull
@Override
public ResolveResult[] resolve(@NotNull PsiJavaReference reference, boolean incompleteCode) {
PsiClass containingClass = null;
final PsiExpression expression = getQualifierExpression();
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
if (expression != null) {
PsiClassType.ClassResolveResult result = PsiUtil.resolveGenericsClassInType(expression.getType());
@Override
public void process(Ref<PsiClass> psiClassRef, Ref<PsiSubstitutor> substRef) {
PsiClass containingClass = null;
final PsiExpression expression = getQualifierExpression();
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
if (expression != null) {
PsiClassType.ClassResolveResult result = PsiUtil.resolveGenericsClassInType(expression.getType());
containingClass = result.getElement();
if (containingClass != null) {
substitutor = result.getSubstitutor();
}
if (containingClass == null && expression instanceof PsiReferenceExpression) {
final PsiElement resolve = ((PsiReferenceExpression)expression).resolve();
if (resolve instanceof PsiClass) {
containingClass = (PsiClass)resolve;
}
}
}
else {
final PsiTypeElement typeElement = getQualifierType();
if (typeElement != null) {
PsiClassType.ClassResolveResult result = PsiUtil.resolveGenericsClassInType(typeElement.getType());
containingClass = result.getElement();
if (containingClass != null) {
substitutor = result.getSubstitutor();
}
if (containingClass == null && expression instanceof PsiReferenceExpression) {
final PsiElement resolve = ((PsiReferenceExpression)expression).resolve();
if (resolve instanceof PsiClass) {
containingClass = (PsiClass)resolve;
}
}
}
else {
final PsiTypeElement typeElement = getQualifierType();
if (typeElement != null) {
PsiClassType.ClassResolveResult result = PsiUtil.resolveGenericsClassInType(typeElement.getType());
containingClass = result.getElement();
if (containingClass != null) {
substitutor = result.getSubstitutor();
}
}
}
}
psiClassRef.set(containingClass);
substRef.set(substitutor);
}
private class MethodReferenceResolver implements ResolveCache.PolyVariantResolver<PsiJavaReference> {
@NotNull
@Override
public ResolveResult[] resolve(@NotNull PsiJavaReference reference, boolean incompleteCode) {
final Ref<PsiClass> classRef = new Ref<PsiClass>();
final Ref<PsiSubstitutor> substRef = new Ref<PsiSubstitutor>();
process(classRef, substRef);
final PsiClass containingClass = classRef.get();
final PsiSubstitutor substitutor = substRef.get();
if (containingClass != null) {
final PsiElement element = getReferenceNameElement();
if (element instanceof PsiIdentifier) {
final PsiType functionalInterfaceType = getFunctionalInterfaceType();
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
final MethodSignature interfaceMethodSignature = interfaceMethod != null ? interfaceMethod.getSignature(resolveResult.getSubstitutor()) : null;
MethodCandidatesProcessor processor = new MethodCandidatesProcessor(PsiMethodReferenceExpressionImpl.this,
new PsiConflictResolver[]{DuplicateConflictResolver.INSTANCE}, new SmartList<CandidateInfo>()) {
@Override
protected boolean isAccepted(PsiMethod candidate) {
if (super.isAccepted(candidate)) {
if (interfaceMethodSignature == null) return true;
return LambdaUtil.areAcceptable(interfaceMethodSignature,
candidate.getSignature(JavaPsiFacade.getElementFactory(getProject()).createRawSubstitutor(candidate)));
}
return false;
}
};
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
final MethodReferenceConflictResolver conflictResolver = new MethodReferenceConflictResolver(containingClass, interfaceMethod, resolveResult.getSubstitutor());
final MethodCandidatesProcessor processor = new MethodCandidatesProcessor(PsiMethodReferenceExpressionImpl.this,
new PsiConflictResolver[]{conflictResolver}, new SmartList<CandidateInfo>());
processor.setIsConstructor(false);
processor.setName(element.getText());
@@ -226,5 +231,36 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
}
return JavaResolveResult.EMPTY_ARRAY;
}
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) {
myContainingClass = containingClass;
myFunctionalInterface = psiMethod;
mySubstitutor = substitutor;
}
@Nullable
@Override
public CandidateInfo resolveConflict(List<CandidateInfo> conflicts) {
if (myFunctionalInterface == 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)) {
iterator.remove();
}
}
if (conflicts.size() == 1) return conflicts.get(0);
return null;
}
}
}
}

View File

@@ -154,3 +154,32 @@ class MyTest5 {
}
}
public class MyTest6 {
interface I {
void _(Integer i);
}
static void foo(Number i) {}
static void foo(Integer i, String s) {}
static void foo(Integer d) {}
public static void main(String[] args) {
I s = MyTest6::foo;
s._(1);
}
}
public class MyTest7 {
interface I {
void _(Number i);
}
static void foo(Number i) {}
static void foo(Integer i, String s) {}
static void foo(Integer d) {}
public static void main(String[] args) {
I s = MyTest7::foo;
s._(1);
}
}

View File

@@ -32,4 +32,11 @@ class Test1 {
interface Bar {
Integer _(String s);
}
}
}
class Test2 {
void foo(Integer i) {}
<error descr="Incompatible types. Found: '<method reference>', required: 'java.lang.Object'">Object o = Test2::foo;</error>
}

View File

@@ -0,0 +1,21 @@
class MyTest {
interface I {
abstract void m1(int i);
}
static class A {
void m(int i) {}
}
static class B extends A {
void m(int i) {
I mh = super::m;
mh.m1(i);
}
}
public static void main(String[] args) {
new B().m(10);
}
}

View File

@@ -0,0 +1,13 @@
class MyTest {
interface I {
void _(MyTest receiver, Integer i);
}
void m(Integer i) {}
public static void main(String[] args) {
I i = MyTest :: m;
i._(new MyTest(), 1);
}
}

View File

@@ -33,6 +33,14 @@ public class MethodRefHighlightingTest extends LightDaemonAnalyzerTestCase {
doTest();
}
public void testMethodReferenceReceiver() throws Exception {
doTest();
}
public void testMethodRefMisc() throws Exception {
doTest();
}
private void doTest() throws Exception {
doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false);
}