mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 13:31:28 +07:00
method references: remove code duplication in return type checks
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
package com.intellij.psi;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.util.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -77,6 +78,90 @@ public class PsiMethodReferenceUtil {
|
||||
return qualifierType;
|
||||
}
|
||||
|
||||
public static boolean isReturnTypeCompatible(PsiMethodReferenceExpression expression,
|
||||
JavaResolveResult result,
|
||||
PsiType functionalInterfaceType) {
|
||||
return isReturnTypeCompatible(expression, result, functionalInterfaceType, null);
|
||||
}
|
||||
|
||||
private static boolean isReturnTypeCompatible(PsiMethodReferenceExpression expression,
|
||||
JavaResolveResult result,
|
||||
PsiType functionalInterfaceType,
|
||||
Ref<String> errorMessage) {
|
||||
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
|
||||
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
|
||||
if (interfaceMethod != null) {
|
||||
final PsiType interfaceReturnType = LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType);
|
||||
|
||||
if (PsiType.VOID.equals(interfaceReturnType) || interfaceReturnType == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PsiSubstitutor subst = result.getSubstitutor();
|
||||
|
||||
PsiType methodReturnType = null;
|
||||
PsiClass containingClass = null;
|
||||
final PsiElement resolve = result.getElement();
|
||||
if (resolve instanceof PsiMethod) {
|
||||
containingClass = ((PsiMethod)resolve).getContainingClass();
|
||||
|
||||
PsiType returnType = PsiTypesUtil.patchMethodGetClassReturnType(expression, expression, (PsiMethod)resolve, null, PsiUtil.getLanguageLevel(expression));
|
||||
|
||||
if (returnType == null) {
|
||||
returnType = ((PsiMethod)resolve).getReturnType();
|
||||
if (PsiType.VOID.equals(returnType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PsiClass qContainingClass = getQualifierResolveResult(expression).getContainingClass();
|
||||
if (qContainingClass != null && containingClass != null &&
|
||||
isReceiverType(getFirstParameterType(functionalInterfaceType, expression), qContainingClass, subst)) {
|
||||
subst = TypeConversionUtil.getClassSubstitutor(containingClass, qContainingClass, subst);
|
||||
LOG.assertTrue(subst != null);
|
||||
}
|
||||
|
||||
methodReturnType = subst.substitute(returnType);
|
||||
if (methodReturnType != null) {
|
||||
methodReturnType = PsiUtil.captureToplevelWildcards(methodReturnType, expression);
|
||||
}
|
||||
}
|
||||
else {
|
||||
methodReturnType = returnType;
|
||||
}
|
||||
}
|
||||
else if (resolve instanceof PsiClass) {
|
||||
if (resolve == JavaPsiFacade.getElementFactory(expression.getProject()).getArrayClass(PsiUtil.getLanguageLevel(resolve))) {
|
||||
final PsiTypeParameter[] typeParameters = ((PsiClass)resolve).getTypeParameters();
|
||||
if (typeParameters.length == 1) {
|
||||
final PsiType arrayComponentType = subst.substitute(typeParameters[0]);
|
||||
if (arrayComponentType == null) {
|
||||
return false;
|
||||
}
|
||||
methodReturnType = arrayComponentType.createArrayType();
|
||||
}
|
||||
}
|
||||
containingClass = (PsiClass)resolve;
|
||||
}
|
||||
|
||||
if (methodReturnType == null) {
|
||||
if (containingClass == null) {
|
||||
return false;
|
||||
}
|
||||
methodReturnType = JavaPsiFacade.getElementFactory(expression.getProject()).createType(containingClass, subst);
|
||||
}
|
||||
|
||||
if (TypeConversionUtil.isAssignable(interfaceReturnType, methodReturnType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (errorMessage != null) {
|
||||
errorMessage.set("Bad return type in method reference: " +
|
||||
"cannot convert " + methodReturnType.getCanonicalText() + " to " + interfaceReturnType.getCanonicalText());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class QualifierResolveResult {
|
||||
private final PsiClass myContainingClass;
|
||||
private final PsiSubstitutor mySubstitutor;
|
||||
@@ -284,35 +369,9 @@ public class PsiMethodReferenceUtil {
|
||||
}
|
||||
|
||||
public static String checkReturnType(PsiMethodReferenceExpression expression, JavaResolveResult result, PsiType functionalInterfaceType) {
|
||||
final PsiElement resolve = result.getElement();
|
||||
if (resolve instanceof PsiMethod) {
|
||||
final PsiClass containingClass = ((PsiMethod)resolve).getContainingClass();
|
||||
LOG.assertTrue(containingClass != null);
|
||||
PsiSubstitutor subst = result.getSubstitutor();
|
||||
PsiClass qContainingClass = getQualifierResolveResult(expression).getContainingClass();
|
||||
if (qContainingClass != null && isReceiverType(getFirstParameterType(functionalInterfaceType, expression), qContainingClass, subst)) {
|
||||
subst = TypeConversionUtil.getClassSubstitutor(containingClass, qContainingClass, subst);
|
||||
LOG.assertTrue(subst != null);
|
||||
}
|
||||
|
||||
|
||||
final PsiType interfaceReturnType = LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType);
|
||||
|
||||
PsiType returnType = PsiTypesUtil.patchMethodGetClassReturnType(expression, expression,
|
||||
(PsiMethod)resolve, null,
|
||||
PsiUtil.getLanguageLevel(expression));
|
||||
if (returnType == null) {
|
||||
returnType = ((PsiMethod)resolve).getReturnType();
|
||||
}
|
||||
PsiType methodReturnType = subst.substitute(returnType);
|
||||
if (interfaceReturnType != null && !PsiType.VOID.equals(interfaceReturnType)) {
|
||||
if (methodReturnType == null) {
|
||||
methodReturnType = JavaPsiFacade.getElementFactory(expression.getProject()).createType(containingClass, subst);
|
||||
}
|
||||
if (!TypeConversionUtil.isAssignable(interfaceReturnType, PsiUtil.captureToplevelWildcards(methodReturnType, expression))) {
|
||||
return "Bad return type in method reference: cannot convert " + methodReturnType.getCanonicalText() + " to " + interfaceReturnType.getCanonicalText();
|
||||
}
|
||||
}
|
||||
final Ref<String> errorMessage = Ref.create();
|
||||
if (!isReturnTypeCompatible(expression, result, functionalInterfaceType, errorMessage)) {
|
||||
return errorMessage.get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -459,65 +459,7 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
|
||||
return false;
|
||||
}
|
||||
|
||||
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(left);
|
||||
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
|
||||
if (interfaceMethod != null) {
|
||||
final PsiType interfaceReturnType = LambdaUtil.getFunctionalInterfaceReturnType(left);
|
||||
|
||||
if (PsiType.VOID.equals(interfaceReturnType) || interfaceReturnType == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PsiSubstitutor subst = result.getSubstitutor();
|
||||
|
||||
PsiType methodReturnType = null;
|
||||
PsiClass containingClass = null;
|
||||
if (resolve instanceof PsiMethod) {
|
||||
containingClass = ((PsiMethod)resolve).getContainingClass();
|
||||
|
||||
PsiType returnType = PsiTypesUtil.patchMethodGetClassReturnType(this, this, (PsiMethod)resolve, null, PsiUtil.getLanguageLevel(this));
|
||||
|
||||
if (returnType == null) {
|
||||
returnType = ((PsiMethod)resolve).getReturnType();
|
||||
}
|
||||
|
||||
if (PsiType.VOID.equals(returnType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PsiClass qContainingClass = PsiMethodReferenceUtil.getQualifierResolveResult(this).getContainingClass();
|
||||
if (qContainingClass != null && containingClass != null &&
|
||||
PsiMethodReferenceUtil.isReceiverType(PsiMethodReferenceUtil.getFirstParameterType(left, this), qContainingClass, subst)) {
|
||||
subst = TypeConversionUtil.getClassSubstitutor(containingClass, qContainingClass, subst);
|
||||
LOG.assertTrue(subst != null);
|
||||
}
|
||||
|
||||
methodReturnType = subst.substitute(returnType);
|
||||
}
|
||||
else if (resolve instanceof PsiClass) {
|
||||
if (resolve == JavaPsiFacade.getElementFactory(resolve.getProject()).getArrayClass(PsiUtil.getLanguageLevel(resolve))) {
|
||||
final PsiTypeParameter[] typeParameters = ((PsiClass)resolve).getTypeParameters();
|
||||
if (typeParameters.length == 1) {
|
||||
final PsiType arrayComponentType = subst.substitute(typeParameters[0]);
|
||||
if (arrayComponentType == null) {
|
||||
return false;
|
||||
}
|
||||
methodReturnType = arrayComponentType.createArrayType();
|
||||
}
|
||||
}
|
||||
containingClass = (PsiClass)resolve;
|
||||
}
|
||||
|
||||
if (methodReturnType == null) {
|
||||
if (containingClass == null) {
|
||||
return false;
|
||||
}
|
||||
methodReturnType = JavaPsiFacade.getElementFactory(getProject()).createType(containingClass, subst);
|
||||
}
|
||||
|
||||
return TypeConversionUtil.isAssignable(interfaceReturnType, methodReturnType);
|
||||
}
|
||||
return false;
|
||||
return PsiMethodReferenceUtil.isReturnTypeCompatible(this, result, left);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -26,7 +26,7 @@ class AlienTest {
|
||||
|
||||
static {
|
||||
IInt i1 = MyTest::<error descr="Cannot resolve method 'abracadabra'">abracadabra</error>;
|
||||
IInt i2 = <error descr="Bad return type in method reference: cannot convert void to int">MyTest::foo</error>;
|
||||
IInt i2 = MyTest::<error descr="Invalid method reference: int cannot be converted to String">foo</error>;
|
||||
IInt i3 = MyTest::<error descr="Cannot resolve method 'bar'">bar</error>;
|
||||
<error descr="Incompatible types. Found: '<method reference>', required: 'AlienTest.IIntInt'">IIntInt i4 = MyTest::bar;</error>
|
||||
IInt i5 = <error descr="Non-static method cannot be referenced from a static context">MyTest::baz</error>;
|
||||
|
||||
@@ -13,7 +13,7 @@ class Foo<R> {
|
||||
|
||||
public void foo() {
|
||||
reduce(Moo::new);
|
||||
reduce<error descr="'reduce(Foo.Factory<S>)' in 'Foo' cannot be applied to '(<method reference>)'">(AMoo::new)</error>;
|
||||
reduce(<error descr="Bad return type in method reference: cannot convert Foo<R>.AMoo to S">AMoo::new</error>);
|
||||
reduce(AAMoo::new);
|
||||
reduce(AAAMoo::new);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ class DefaultConstructor {
|
||||
|
||||
void test2() {
|
||||
I1<Inner> i1 = Inner :: new;
|
||||
<error descr="Incompatible types. Found: '<method reference>', required: 'DefaultConstructor.I1<java.lang.Integer>'">I1<Integer> i1Int = Inner :: new;</error>
|
||||
I1<Integer> i1Int = <error descr="Bad return type in method reference: cannot convert DefaultConstructor.Outer.Inner to java.lang.Integer">Inner :: new</error>;
|
||||
I2<Inner, Outer> i2 = Inner :: <error descr="Cannot resolve constructor 'Inner'">new</error>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class OnArrayTest {
|
||||
|
||||
ArrayReturnType<String[]> a1 = String[]::new;
|
||||
ArrayReturnType<String[][]> a2 = String[][]::new;
|
||||
<error descr="Incompatible types. Found: '<method reference>', required: 'OnArrayTest.ArrayReturnType<java.lang.String[]>'">ArrayReturnType<String[]> a3 = int[]::new;</error>
|
||||
ArrayReturnType<String[]> a3 = <error descr="Bad return type in method reference: cannot convert int[] to java.lang.String[]">int[]::new</error>;
|
||||
|
||||
ObjectArrayReturnType a4 = Foo<?>[]::new;
|
||||
ObjectArrayReturnType a5 = <error descr="Generic array creation">Foo<? extends String>[]</error>::new;
|
||||
|
||||
Reference in New Issue
Block a user