method refs: simplify if first parameter is a receiver for method reference (IDEA-136882)

This commit is contained in:
Anna Kozlova
2015-02-24 15:04:54 +01:00
parent 94787a5f67
commit ab8c437a33
5 changed files with 54 additions and 48 deletions
@@ -263,13 +263,14 @@ public class LambdaCanBeMethodReferenceInspection extends BaseJavaBatchLocalInsp
if (element instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)element;
final PsiMethod psiMethod = methodCall.resolveMethod();
JavaResolveResult result = methodCall.resolveMethodGenerics();
final PsiMethod psiMethod = (PsiMethod)result.getElement();
if (psiMethod == null) {
return null;
}
final PsiReferenceExpression methodExpression = methodCall.getMethodExpression();
final String qualifierByMethodCall = getQualifierTextByMethodCall(methodCall, functionalInterfaceType, parameters, psiMethod);
final String qualifierByMethodCall = getQualifierTextByMethodCall(methodCall, functionalInterfaceType, parameters, psiMethod, result.getSubstitutor());
if (qualifierByMethodCall != null) {
return qualifierByMethodCall + "::" + ((PsiMethodCallExpression)element).getTypeArgumentList().getText() + methodExpression.getReferenceName();
}
@@ -324,7 +325,8 @@ public class LambdaCanBeMethodReferenceInspection extends BaseJavaBatchLocalInsp
private static String getQualifierTextByMethodCall(final PsiMethodCallExpression methodCall,
final PsiType functionalInterfaceType,
final PsiParameter[] parameters,
final PsiMethod psiMethod) {
final PsiMethod psiMethod,
final PsiSubstitutor substitutor) {
final PsiExpression qualifierExpression = methodCall.getMethodExpression().getQualifierExpression();
@@ -332,7 +334,7 @@ public class LambdaCanBeMethodReferenceInspection extends BaseJavaBatchLocalInsp
LOG.assertTrue(containingClass != null);
if (qualifierExpression != null) {
boolean isReceiverType = PsiMethodReferenceUtil.isReceiverType(functionalInterfaceType, containingClass, psiMethod);
boolean isReceiverType = PsiMethodReferenceUtil.isReceiverType(PsiMethodReferenceUtil.getFirstParameterType(functionalInterfaceType, methodCall), containingClass, substitutor);
return isReceiverType ? composeReceiverQualifierText(parameters, psiMethod, containingClass, qualifierExpression)
: qualifierExpression.getText();
}
@@ -422,7 +424,6 @@ public class LambdaCanBeMethodReferenceInspection extends BaseJavaBatchLocalInsp
if (lambdaExpression == null) return;
PsiType functionalInterfaceType = lambdaExpression.getFunctionalInterfaceType();
if (functionalInterfaceType == null || !functionalInterfaceType.isValid()) return;
String functionalTypeText = functionalInterfaceType.getCanonicalText();
final String methodRefText = createMethodReferenceText(element, functionalInterfaceType,
lambdaExpression.getParameterList().getParameters());
@@ -30,10 +30,10 @@ public class PsiMethodReferenceUtil {
public static final Logger LOG = Logger.getInstance("#" + PsiMethodReferenceUtil.class.getName());
public static boolean hasReceiver(PsiType[] parameterTypes, QualifierResolveResult qualifierResolveResult, PsiMethodReferenceExpression methodRef) {
if (parameterTypes.length > 0 &&
if (parameterTypes.length > 0 &&
!methodRef.isConstructor() &&
isReceiverType(parameterTypes[0], qualifierResolveResult.getContainingClass(), qualifierResolveResult.getSubstitutor()) &&
isStaticallyReferenced(methodRef)) {
isStaticallyReferenced(methodRef) &&
isReceiverType(parameterTypes[0], qualifierResolveResult.getContainingClass(), qualifierResolveResult.getSubstitutor())) {
return true;
}
return false;
@@ -46,7 +46,7 @@ public class PsiMethodReferenceUtil {
LOG.assertTrue(containingClass != null);
PsiSubstitutor subst = result.getSubstitutor();
PsiClass qContainingClass = getQualifierResolveResult(expression).getContainingClass();
if (qContainingClass != null && isReceiverType(functionalInterfaceType, containingClass, (PsiMethod)resolve)) {
if (qContainingClass != null && isReceiverType(getFirstParameterType(functionalInterfaceType, expression), qContainingClass, subst)) {
subst = TypeConversionUtil.getClassSubstitutor(containingClass, qContainingClass, subst);
LOG.assertTrue(subst != null);
}
@@ -202,53 +202,27 @@ public class PsiMethodReferenceUtil {
return true;
}
public static boolean isReceiverType(@Nullable PsiClass aClass, @Nullable PsiClass containingClass) {
return InheritanceUtil.isInheritorOrSelf(aClass, containingClass, true);
}
public static boolean isReceiverType(PsiType receiverType, @Nullable PsiClass containingClass, PsiSubstitutor psiSubstitutor) {
if (containingClass != null) {
receiverType = getExpandedType(receiverType, containingClass);
//if P1, ..., Pn is not empty and P1 is a subtype of ReferenceType, then the method reference expression is treated as
// if it were a method invocation expression with argument expressions of types P2, ...,Pn.
public static boolean isReceiverType(@Nullable PsiType receiverType, PsiClass containingClass, PsiSubstitutor psiSubstitutor) {
if (receiverType == null) {
return false;
}
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(receiverType);
final PsiClass receiverClass = resolveResult.getElement();
if (receiverClass != null && isReceiverType(receiverClass, containingClass)) {
if (emptyOrRaw(containingClass, psiSubstitutor)) {
return true;
}
final PsiSubstitutor derivedSubstitutor = TypeConversionUtil.getClassSubstitutor(containingClass, receiverClass, psiSubstitutor);
return derivedSubstitutor != null && TypeConversionUtil.isAssignable(JavaPsiFacade.getElementFactory(containingClass.getProject()).createType(containingClass, derivedSubstitutor), receiverType);
}
return false;
return TypeConversionUtil.isAssignable(JavaPsiFacade.getElementFactory(containingClass.getProject()).createType(containingClass, psiSubstitutor),
getExpandedType(receiverType, containingClass));
}
private static boolean emptyOrRaw(PsiClass containingClass, PsiSubstitutor psiSubstitutor) {
return PsiUtil.isRawSubstitutor(containingClass, psiSubstitutor) ||
psiSubstitutor.getSubstitutionMap().isEmpty();
}
public static boolean isReceiverType(PsiType functionalInterfaceType, PsiClass containingClass, @Nullable PsiMethod referencedMethod) {
public static PsiType getFirstParameterType(PsiType functionalInterfaceType, PsiElement context) {
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
final MethodSignature function = LambdaUtil.getFunction(resolveResult.getElement());
if (function != null) {
final int interfaceMethodParamsLength = function.getParameterTypes().length;
if (interfaceMethodParamsLength > 0) {
final PsiType firstParamType = resolveResult.getSubstitutor().substitute(function.getParameterTypes()[0]);
boolean isReceiver = isReceiverType(firstParamType,
containingClass, PsiUtil.resolveGenericsClassInType(firstParamType).getSubstitutor());
if (isReceiver) {
if (referencedMethod == null){
if (interfaceMethodParamsLength == 1) return true;
return false;
}
if (referencedMethod.getParameterList().getParametersCount() != interfaceMethodParamsLength - 1) {
return false;
}
return true;
}
PsiType type = resolveResult.getSubstitutor().substitute(function.getParameterTypes()[0]);
return type != null ? PsiUtil.captureToplevelWildcards(type, context) : null;
}
}
return false;
return null;
}
private static PsiType getExpandedType(PsiType type, @NotNull PsiElement typeElement) {
@@ -20,7 +20,6 @@ import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil;
@@ -480,7 +479,7 @@ public class PsiMethodReferenceExpressionImpl extends PsiReferenceExpressionBase
PsiClass qContainingClass = PsiMethodReferenceUtil.getQualifierResolveResult(this).getContainingClass();
if (qContainingClass != null && containingClass != null &&
PsiMethodReferenceUtil.isReceiverType(left, containingClass, (PsiMethod)resolve)) {
PsiMethodReferenceUtil.isReceiverType(PsiMethodReferenceUtil.getFirstParameterType(left, this), qContainingClass, subst)) {
subst = TypeConversionUtil.getClassSubstitutor(containingClass, qContainingClass, subst);
LOG.assertTrue(subst != null);
}
@@ -0,0 +1,28 @@
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
class Test {
static class Base {
public BigDecimal getTotal() {
return null;
}
}
public static void foo(List<? extends Base> list, List<Base> list1) {
System.out.println(add(list, Base::getTotal));
list1.forEach(Base::getTotal);
}
public static <T> BigDecimal add(Collection<T> objectsThatHaveBigDecimals, Function<T, ? extends BigDecimal> functionToGet) {
return objectsThatHaveBigDecimals == null ? null : objectsThatHaveBigDecimals.stream().map(functionToGet).reduce(null, Test::add);
}
public static BigDecimal add(BigDecimal... sequence) {
return null;
}
}
@@ -382,6 +382,10 @@ public class NewMethodRefHighlightingTest extends LightDaemonAnalyzerTestCase {
doTest(false);
}
public void testSecondSearchIfFirstParameterIsASubtypeOfReferenceTypeFromExpressionDeclaration() throws Exception {
doTest();
}
private void doTest() {
doTest(false);
}