mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 13:31:28 +07:00
method refs: ambiguity checks into resolver; testdata
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user