diff --git a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java index 51195c87f996..7a13bf5fba95 100644 --- a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java @@ -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 classRef = new Ref(); + methodReferenceExpression.process(classRef, new Ref()); + 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; } } diff --git a/java/java-psi-api/src/com/intellij/psi/PsiMethodReferenceExpression.java b/java/java-psi-api/src/com/intellij/psi/PsiMethodReferenceExpression.java index 5f7cb94c783b..94ac6b3cb96d 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiMethodReferenceExpression.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiMethodReferenceExpression.java @@ -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 psiClassRef, Ref substRef); } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java index d382542ed540..4bc53329cb36 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiMethodReferenceExpressionImpl.java @@ -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 { - @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 psiClassRef, Ref 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 { + @NotNull + @Override + public ResolveResult[] resolve(@NotNull PsiJavaReference reference, boolean incompleteCode) { + final Ref classRef = new Ref(); + final Ref substRef = new Ref(); + 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()) { - @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()); 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 conflicts) { + if (myFunctionalInterface == null) return null; + + for (Iterator 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; + } + } } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/Ambiguity.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/Ambiguity.java index 77a5312d4587..d11a1d6a6cc0 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/Ambiguity.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/Ambiguity.java @@ -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); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/Assignability.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/Assignability.java index 5a0884b17b87..6b8d1fbfa6a5 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/Assignability.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/Assignability.java @@ -32,4 +32,11 @@ class Test1 { interface Bar { Integer _(String s); } -} \ No newline at end of file +} + +class Test2 { + + void foo(Integer i) {} + + Object o = Test2::foo; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/MethodRefMisc.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/MethodRefMisc.java new file mode 100644 index 000000000000..9adc00a43b75 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/MethodRefMisc.java @@ -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); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/MethodReferenceReceiver.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/MethodReferenceReceiver.java new file mode 100644 index 000000000000..9e852244b1b8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/methodRef/MethodReferenceReceiver.java @@ -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); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java index b4e0cb1b4db5..badf2a25e22b 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java @@ -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); }