mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 13:31:28 +07:00
method ref: isAssignable check; resolve (initial)
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user