method ref: isAssignable check; resolve (initial)

This commit is contained in:
anna
2012-09-25 19:44:27 +02:00
parent 71eb8ec93e
commit d1fbf297bc
7 changed files with 158 additions and 7 deletions

View File

@@ -16,6 +16,7 @@
package com.intellij.psi;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.infos.MethodCandidateInfo;
@@ -560,6 +561,37 @@ public class LambdaUtil {
return true;
}
public static boolean isAcceptable(PsiMethodReferenceExpression methodReferenceExpression, PsiClassType left) {
final JavaResolveResult[] results = methodReferenceExpression.multiResolve(false);
for (JavaResolveResult result : results) {
final PsiElement resolve = result.getElement();
if (resolve instanceof PsiMethod) {
final PsiClassType.ClassResolveResult resolveResult = left.resolveGenerics();
final PsiMethod method = getFunctionalInterfaceMethod(resolveResult);
if (method != null) {
final MethodSignature signature1 = method.getSignature(resolveResult.getSubstitutor());
final MethodSignature signature2 = ((PsiMethod)resolve).getSignature(result.getSubstitutor());
if (areAcceptable(signature1, signature2)) return true;
}
}
}
return false;
}
public static boolean areAcceptable(MethodSignature signature1, MethodSignature signature2) {
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];
final PsiType type2 = signatureParameterTypes2[i];
if (!Comparing.equal(GenericsUtil.eliminateWildcards(type1), GenericsUtil.eliminateWildcards(type2))) {
return false;
}
}
return true;
}
private static class TypeParamsChecker extends PsiTypeVisitor<Boolean> {
private PsiMethod myMethod;
private final PsiClass myClass;

View File

@@ -652,8 +652,12 @@ public class TypeConversionUtil {
return !(left instanceof PsiPrimitiveType) || isNullType(left);
}
// todo[r.sh] implement
if (right instanceof PsiMethodReferenceType && left instanceof PsiClassType) return true;
if (right instanceof PsiMethodReferenceType) {
final PsiMethodReferenceExpression methodReferenceExpression = ((PsiMethodReferenceType)right).getExpression();
if (left instanceof PsiClassType) {
return LambdaUtil.isAcceptable(methodReferenceExpression, (PsiClassType)left);
}
}
if (right instanceof PsiLambdaExpressionType) {
final PsiLambdaExpression rLambdaExpression = ((PsiLambdaExpressionType)right).getExpression();
if (left instanceof PsiClassType) {

View File

@@ -18,10 +18,18 @@ package com.intellij.psi.impl.source.tree.java;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiManagerEx;
import com.intellij.psi.impl.source.resolve.ResolveCache;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
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");
@@ -36,6 +44,12 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
return qualifier instanceof PsiTypeElement ? (PsiTypeElement)qualifier : null;
}
@Nullable
@Override
public PsiType getFunctionalInterfaceType() {
return LambdaUtil.getFunctionalInterfaceType(this, true);
}
@Override
public PsiExpression getQualifierExpression() {
final PsiElement qualifier = getQualifier();
@@ -61,8 +75,17 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
@NotNull
@Override
public JavaResolveResult[] multiResolve(final boolean incompleteCode) {
// todo[r.sh]: implement
return JavaResolveResult.EMPTY_ARRAY;
final PsiManagerEx manager = getManager();
if (manager == null) {
LOG.error("getManager() == null!");
return JavaResolveResult.EMPTY_ARRAY;
}
if (!isValid()) {
LOG.error("invalid!");
return JavaResolveResult.EMPTY_ARRAY;
}
ResolveResult[] results = ResolveCache.getInstance(getProject()).resolveWithCaching(this, new MethodReferenceResolver(), true, incompleteCode);
return results.length == 0 ? JavaResolveResult.EMPTY_ARRAY : (JavaResolveResult[])results;
}
@Override
@@ -74,9 +97,15 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
@Override
public TextRange getRangeInElement() {
final PsiElement element = getReferenceNameElement();
if (element != null) return new TextRange(element.getStartOffsetInParent(), element.getTextLength());
if (element != null) {
final int offsetInParent = element.getStartOffsetInParent();
return new TextRange(offsetInParent, offsetInParent + element.getTextLength());
}
final PsiElement colons = findPsiChildByType(JavaTokenType.DOUBLE_COLON);
if (colons != null) return new TextRange(colons.getStartOffsetInParent(), colons.getTextLength());
if (colons != null) {
final int offsetInParent = colons.getStartOffsetInParent();
return new TextRange(offsetInParent, offsetInParent + colons.getTextLength());
}
LOG.error(getText());
return null;
}
@@ -117,4 +146,48 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
public String toString() {
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();
if (expression != null) {
containingClass = PsiUtil.resolveClassInType(expression.getType());
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) {
containingClass = PsiUtil.resolveClassInType(typeElement.getType());
}
}
if (containingClass != null) {
final PsiElement element = getReferenceNameElement();
if (element instanceof PsiIdentifier) {
final PsiType functionalInterfaceType = getFunctionalInterfaceType();
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
if (interfaceMethod == null) return JavaResolveResult.EMPTY_ARRAY;
final MethodSignature interfaceMethodSignature = interfaceMethod.getSignature(resolveResult.getSubstitutor());
final PsiMethod[] psiMethods = containingClass.findMethodsByName(element.getText(), false);
List<JavaResolveResult> result = new ArrayList<JavaResolveResult>();
for (PsiMethod method : psiMethods) {
if (LambdaUtil.areAcceptable(interfaceMethodSignature, method.getSignature(PsiSubstitutor.EMPTY))) {
result.add(new CandidateInfo(method, PsiSubstitutor.EMPTY));
}
}
return result.toArray(new JavaResolveResult[result.size()]);
}
}
return JavaResolveResult.EMPTY_ARRAY;
}
}
}

View File

@@ -262,6 +262,9 @@ public class PsiReferenceExpressionImpl extends PsiReferenceExpressionBase imple
if (parentType == JavaElementType.METHOD_CALL_EXPRESSION) {
return resolveToMethod();
}
if (parentType == JavaElementType.METHOD_REF_EXPRESSION) {
return resolve(JavaElementType.REFERENCE_EXPRESSION);
}
return resolveToVariable();
}

View File

@@ -0,0 +1,35 @@
class Test {
{
<error descr="Incompatible types. Found: '<method reference>', required: 'java.lang.Runnable'">Runnable b = Test :: length;</error>
Comparable<String> c = Test :: length;
<error descr="Incompatible types. Found: '<method reference>', required: 'java.lang.Comparable<java.lang.Integer>'">Comparable<Integer> c1 = Test :: length;</error>
}
public static Integer length(String s) {
return s.length();
}
interface Bar {
Integer _(String s);
}
}
class Test1 {
{
<error descr="Incompatible types. Found: '<method reference>', required: 'java.lang.Runnable'">Runnable b = Test1 :: length;</error>
Comparable<String> c = Test1 :: length;
Comparable<Integer> c1 = Test1 :: length;
}
public static Integer length(String s) {
return s.length();
}
public static Integer length(Integer s) {
return s;
}
interface Bar {
Integer _(String s);
}
}

View File

@@ -4,7 +4,7 @@ class A {
f(A::foo);
}
private void f(Object foo) {
private void f(Object p0) {
<selection>//To change body of created methods use File | Settings | File Templates.</selection>
}

View File

@@ -25,6 +25,10 @@ public class MethodRefHighlightingTest extends LightDaemonAnalyzerTestCase {
doTest();
}
public void testAssignability() throws Exception {
doTest();
}
private void doTest() throws Exception {
doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false);
}