lambda -> method ref: do not convert if resulted ref would be ambiguous (IDEA-116964)

This commit is contained in:
anna
2013-11-28 21:42:31 +01:00
parent 9859872004
commit 3c78ab4dc5
4 changed files with 65 additions and 8 deletions
@@ -118,7 +118,7 @@ public class AnonymousCanBeMethodReferenceInspection extends BaseJavaBatchLocalI
.canBeMethodReferenceProblem(methods[0].getBody(), parameters, anonymousClass.getBaseClassType());
if (callExpression == null) return;
final String methodRefText =
LambdaCanBeMethodReferenceInspection.createMethodReferenceText(callExpression, anonymousClass.getBaseClassType());
LambdaCanBeMethodReferenceInspection.createMethodReferenceText(callExpression, anonymousClass.getBaseClassType(), parameters);
if (methodRefText != null) {
final String canonicalText = anonymousClass.getBaseClassType().getCanonicalText();
@@ -24,6 +24,7 @@ import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.ArrayUtilRt;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
@@ -114,8 +115,8 @@ public class LambdaCanBeMethodReferenceInspection extends BaseJavaBatchLocalInsp
if (argumentList != null) {
final PsiExpression[] expressions = argumentList.getExpressions();
final PsiMethod psiMethod = methodCall.resolveMethod();
final PsiClass containingClass;
PsiMethod psiMethod = methodCall.resolveMethod();
PsiClass containingClass;
boolean isConstructor;
if (psiMethod == null) {
isConstructor = true;
@@ -131,6 +132,12 @@ public class LambdaCanBeMethodReferenceInspection extends BaseJavaBatchLocalInsp
}
if (containingClass == null) return null;
boolean isReceiverType = PsiMethodReferenceUtil.isReceiverType(functionalInterfaceType, containingClass, psiMethod);
if (isReceiverType && psiMethod != null) {
PsiMethod nonAmbiguousMethod = ensureNonAmbiguousMethod(parameters, psiMethod);
if (nonAmbiguousMethod == null) return null;
psiMethod = nonAmbiguousMethod;
containingClass = nonAmbiguousMethod.getContainingClass();
}
final boolean staticOrValidConstructorRef;
if (isConstructor) {
staticOrValidConstructorRef =
@@ -206,13 +213,35 @@ public class LambdaCanBeMethodReferenceInspection extends BaseJavaBatchLocalInsp
}
@Nullable
protected static String createMethodReferenceText(PsiElement element, PsiType functionalInterfaceType) {
private static PsiMethod ensureNonAmbiguousMethod(PsiParameter[] parameters, @NotNull PsiMethod psiMethod) {
String methodName = psiMethod.getName();
PsiClass containingClass = psiMethod.getContainingClass();
if (containingClass == null) return null;
for (PsiMethod method : containingClass.findMethodsByName(methodName, false)) {
PsiParameter[] candidateParams = method.getParameterList().getParameters();
if (candidateParams.length == 1) {
if (TypeConversionUtil.areTypesConvertible(candidateParams[0].getType(), parameters[0].getType())) {
for (PsiMethod superMethod : psiMethod.findDeepestSuperMethods()) {
PsiMethod validSuperMethod = ensureNonAmbiguousMethod(parameters, superMethod);
if (validSuperMethod != null) return validSuperMethod;
}
return null;
}
}
}
return psiMethod;
}
@Nullable
protected static String createMethodReferenceText(final PsiElement element,
final PsiType functionalInterfaceType,
final PsiParameter[] parameters) {
String methodRefText = null;
if (element instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)element;
final PsiMethod psiMethod = methodCall.resolveMethod();
if (psiMethod == null) return null;
final PsiClass containingClass = psiMethod.getContainingClass();
PsiClass containingClass = psiMethod.getContainingClass();
LOG.assertTrue(containingClass != null);
final PsiReferenceExpression methodExpression = methodCall.getMethodExpression();
final PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
@@ -221,8 +250,15 @@ public class LambdaCanBeMethodReferenceInspection extends BaseJavaBatchLocalInsp
boolean isReceiverType = PsiMethodReferenceUtil.isReceiverType(functionalInterfaceType, containingClass, psiMethod);
final String qualifier;
if (isReceiverType) {
final PsiType qualifierExpressionType = qualifierExpression.getType();
qualifier = qualifierExpressionType != null ? qualifierExpressionType.getCanonicalText() : getClassReferenceName(containingClass);
final PsiMethod nonAmbiguousMethod = ensureNonAmbiguousMethod(parameters, psiMethod);
LOG.assertTrue(nonAmbiguousMethod != null);
final PsiClass nonAmbiguousContainingClass = nonAmbiguousMethod.getContainingClass();
if (!containingClass.equals(nonAmbiguousContainingClass)) {
qualifier = getClassReferenceName(nonAmbiguousContainingClass);
} else {
final PsiType qualifierExpressionType = qualifierExpression.getType();
qualifier = qualifierExpressionType != null ? qualifierExpressionType.getCanonicalText() : getClassReferenceName(nonAmbiguousContainingClass);
}
}
else {
qualifier = qualifierExpression.getText();
@@ -298,7 +334,8 @@ public class LambdaCanBeMethodReferenceInspection extends BaseJavaBatchLocalInsp
final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(element, PsiLambdaExpression.class);
if (lambdaExpression == null) return;
final PsiType functionalInterfaceType = lambdaExpression.getFunctionalInterfaceType();
final String methodRefText = createMethodReferenceText(element, functionalInterfaceType);
final String methodRefText = createMethodReferenceText(element, functionalInterfaceType,
lambdaExpression.getParameterList().getParameters());
if (methodRefText != null) {
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
@@ -0,0 +1,10 @@
// "Replace lambda with method reference" "true"
class Example {
interface I {
String foo(Integer i);
}
{
I i = Object::toString;
}
}
@@ -0,0 +1,10 @@
// "Replace lambda with method reference" "true"
class Example {
interface I {
String foo(Integer i);
}
{
I i = (i1) -> i1.<caret>toString();
}
}