From 341b97a5b20a4550d733804b41e2686f62497ea9 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Fri, 23 Dec 2016 14:19:30 +0100 Subject: [PATCH] exact method refs: mark as not exact if on raw type --- .../PsiMethodReferenceExpressionImpl.java | 156 +++++++----------- ...NonExactMethodReferenceOnRawClassType.java | 14 ++ .../lambda/NewMethodRefHighlightingTest.java | 4 + 3 files changed, 78 insertions(+), 96 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/NonExactMethodReferenceOnRawClassType.java 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 c1ab3aaeca52..57a922183616 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 @@ -28,7 +28,6 @@ import com.intellij.psi.impl.java.stubs.JavaStubElementTypes; import com.intellij.psi.impl.source.JavaStubPsiElement; import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil; import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; -import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.infos.MethodCandidateInfo; import com.intellij.psi.scope.ElementClassFilter; @@ -40,12 +39,13 @@ import com.intellij.psi.scope.util.PsiScopesUtil; import com.intellij.psi.util.*; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; -import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; public class PsiMethodReferenceExpressionImpl extends JavaStubPsiElement> implements PsiMethodReferenceExpression { @@ -147,105 +147,69 @@ public class PsiMethodReferenceExpressionImpl extends JavaStubPsiElement result = new ArrayList(); - for (HierarchicalMethodSignature signature : containingClass.getVisibleSignatures()) { - if (identifierName.equals(signature.getName())) { - result.add(signature.getMethod()); - } - } + if (containingClass == null) return null; - if (result.isEmpty()) { - return null; - } - methods = result.toArray(new PsiMethod[result.size()]); + //If the method reference expression has the form ReferenceType::[TypeArguments]Identifier or ClassType::[TypeArguments]new, + //then ReferenceType does not denote a raw type. + final PsiElement qualifier = getQualifier(); + if (qualifier instanceof PsiReferenceExpression) { + PsiElement resolve = ((PsiReferenceExpression)qualifier).resolve(); + if (resolve instanceof PsiClass && ((PsiClass)resolve).hasTypeParameters()) { + return null; } - else if (isConstructor()) { - final PsiElementFactory factory = JavaPsiFacade.getElementFactory(getProject()); - final PsiClass arrayClass = factory.getArrayClass(PsiUtil.getLanguageLevel(this)); - if (arrayClass == containingClass) { - final PsiType componentType = qualifierResolveResult.getSubstitutor().substitute(arrayClass.getTypeParameters()[0]); - LOG.assertTrue(componentType != null, qualifierResolveResult.getSubstitutor()); - //15.13.1 A method reference expression of the form ArrayType :: new is always exact. - return factory.createMethodFromText("public " + componentType.createArrayType().getCanonicalText() + " __array__(int i) {return null;}", this); - } - else { - if (getQualifierType() == null) { - //ClassType is raw or is a non-static member type of a raw type. - PsiClass aClass = containingClass; - while (aClass != null) { - if (aClass.hasTypeParameters()) { - return null; - } + } - if (aClass.hasModifierProperty(PsiModifier.STATIC)) { - break; - } - - aClass = aClass.getContainingClass(); - - if (PsiTreeUtil.isAncestor(aClass, this, true)) { - break; - } - } - } - methods = containingClass.getConstructors(); + PsiMethod[] methods = null; + if (element instanceof PsiIdentifier) { + final String identifierName = element.getText(); + final List result = new ArrayList(); + for (HierarchicalMethodSignature signature : containingClass.getVisibleSignatures()) { + if (identifierName.equals(signature.getName())) { + result.add(signature.getMethod()); } } - if (methods != null) { - PsiMethod psiMethod = null; - if (methods.length > 0) { - for (PsiMethod method : methods) { - if (PsiUtil.isAccessible(method, this, null)) { - if (psiMethod != null) return null; - psiMethod = method; - } - } - if (psiMethod == null) return null; - if (psiMethod.isVarArgs()) return null; - if (psiMethod.getTypeParameters().length > 0) { - final PsiReferenceParameterList parameterList = getParameterList(); - return parameterList != null && parameterList.getTypeParameterElements().length > 0 ? psiMethod : null; - } else { - final PsiSubstitutor classSubstitutor = TypeConversionUtil.getClassSubstitutor(psiMethod.getContainingClass(), containingClass, PsiSubstitutor.EMPTY); - final Set signature = new HashSet(Arrays.asList(psiMethod.getSignature(PsiSubstitutor.EMPTY).getParameterTypes())); - signature.add(psiMethod.getReturnType()); - boolean free = true; - for (PsiType type : signature) { - if (classSubstitutor != null) { - type = classSubstitutor.substitute(type); - } - if (type != null && PsiPolyExpressionUtil.mentionsTypeParameters(type, ContainerUtil.newHashSet(containingClass.getTypeParameters()))) { - free = false; - break; - } - } - if (free) return psiMethod; - } - } - if (containingClass.hasTypeParameters()) { - final PsiElement qualifier = getQualifier(); - PsiJavaCodeReferenceElement referenceElement = null; - if (qualifier instanceof PsiTypeElement) { - referenceElement = ((PsiTypeElement)qualifier).getInnermostComponentReferenceElement(); - } else if (qualifier instanceof PsiReferenceExpression) { - final PsiReferenceExpression expression = (PsiReferenceExpression)qualifier; - if (qualifierResolveResult.isReferenceTypeQualified()) { - referenceElement = expression; - } - } - if (referenceElement != null) { - final PsiReferenceParameterList parameterList = referenceElement.getParameterList(); - if (parameterList == null || parameterList.getTypeParameterElements().length == 0) { - return null; - } - } - } - return psiMethod == null ? containingClass : psiMethod; + + if (result.isEmpty()) { + return null; } + methods = result.toArray(new PsiMethod[result.size()]); + } + else if (isConstructor()) { + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(getProject()); + final PsiClass arrayClass = factory.getArrayClass(PsiUtil.getLanguageLevel(this)); + if (arrayClass == containingClass) { + final PsiType componentType = qualifierResolveResult.getSubstitutor().substitute(arrayClass.getTypeParameters()[0]); + LOG.assertTrue(componentType != null, qualifierResolveResult.getSubstitutor()); + //15.13.1 A method reference expression of the form ArrayType :: new is always exact. + return factory.createMethodFromText("public " + componentType.createArrayType().getCanonicalText() + " __array__(int i) {return null;}", this); + } + else { + methods = containingClass.getConstructors(); + } + } + + if (methods != null) { + PsiMethod psiMethod = null; + if (methods.length > 0) { + //The type to search has exactly one member method with the name Identifier/constructor that is accessible to the class or interface + // in which the method reference expression appears + for (PsiMethod method : methods) { + if (PsiUtil.isAccessible(method, this, null)) { + if (psiMethod != null) return null; + psiMethod = method; + } + } + if (psiMethod == null) return null; + // not variable arity + if (psiMethod.isVarArgs()) return null; + + //If this method/constructor is generic (ยง8.4.4), then the method reference expression provides TypeArguments. + if (psiMethod.getTypeParameters().length > 0) { + final PsiReferenceParameterList parameterList = getParameterList(); + return parameterList != null && parameterList.getTypeParameterElements().length > 0 ? psiMethod : null; + } + } + return psiMethod == null ? containingClass : psiMethod; } return null; } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/NonExactMethodReferenceOnRawClassType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/NonExactMethodReferenceOnRawClassType.java new file mode 100644 index 000000000000..ec0e2dd5daf0 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/NonExactMethodReferenceOnRawClassType.java @@ -0,0 +1,14 @@ +import java.util.function.Consumer; + +class InlineRef { + void foo(Consumer f) {} + + void bar(){ + foo(Descriptor::getName); + } +} + + +class Descriptor { + static void getName(Descriptor d) {} +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java index 4acf09e0f382..5ecfe0e68bfb 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java @@ -533,6 +533,10 @@ public class NewMethodRefHighlightingTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testNonExactMethodReferenceOnRawClassType() throws Exception { + doTest(); + } + private void doTest() { doTest(false); }