exact method refs: mark as not exact if on raw type

This commit is contained in:
Anna.Kozlova
2016-12-23 19:37:48 +01:00
parent 1497fa167b
commit 341b97a5b2
3 changed files with 78 additions and 96 deletions
@@ -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<FunctionalExpressionStub<PsiMethodReferenceExpression>>
implements PsiMethodReferenceExpression {
@@ -147,105 +147,69 @@ public class PsiMethodReferenceExpressionImpl extends JavaStubPsiElement<Functio
final PsiElement element = getReferenceNameElement();
final PsiMethodReferenceUtil.QualifierResolveResult qualifierResolveResult = PsiMethodReferenceUtil.getQualifierResolveResult(this);
final PsiClass containingClass = qualifierResolveResult.getContainingClass();
if (containingClass != null) {
PsiMethod[] methods = null;
if (element instanceof PsiIdentifier) {
final String identifierName = element.getText();
final List<PsiMethod> result = new ArrayList<PsiMethod>();
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<PsiMethod> result = new ArrayList<PsiMethod>();
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<PsiType> signature = new HashSet<PsiType>(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;
}
@@ -0,0 +1,14 @@
import java.util.function.Consumer;
class InlineRef {
<K> void foo(Consumer<K> f) {}
void bar(){
foo(Descriptor::<error descr="Invalid method reference: Object cannot be converted to Descriptor">getName</error>);
}
}
class Descriptor<Y> {
static void getName(Descriptor d) {}
}
@@ -533,6 +533,10 @@ public class NewMethodRefHighlightingTest extends LightDaemonAnalyzerTestCase {
doTest();
}
public void testNonExactMethodReferenceOnRawClassType() throws Exception {
doTest();
}
private void doTest() {
doTest(false);
}