lambda -> method ref: allow conversion with any qualifier, show warning only for trivial qualifiers; same for anonymous -> method ref (IDEA-CR-16052)

This commit is contained in:
Anna.Kozlova
2016-11-22 20:22:29 +01:00
parent 1a1d93d60a
commit 2514f10f0e
4 changed files with 45 additions and 33 deletions
@@ -83,10 +83,10 @@ public class AnonymousCanBeMethodReferenceInspection extends BaseJavaBatchLocalI
if (AnonymousCanBeLambdaInspection.canBeConvertedToLambda(aClass, true, reportNotAnnotatedInterfaces, Collections.emptySet())) {
final PsiMethod method = aClass.getMethods()[0];
final PsiCodeBlock body = method.getBody();
LambdaCanBeMethodReferenceInspection methodReferenceInspection = new LambdaCanBeMethodReferenceInspection();
PsiExpression lambdaBodyCandidate = methodReferenceInspection.extractMethodReferenceCandidateExpression(body, false);
final PsiExpression methodRefCandidate =
new LambdaCanBeMethodReferenceInspection()
.canBeMethodReferenceProblem(body, method.getParameterList().getParameters(), aClass.getBaseClassType(),
aClass.getParent());
methodReferenceInspection.canBeMethodReferenceProblem(method.getParameterList().getParameters(), aClass.getBaseClassType(), aClass.getParent(), lambdaBodyCandidate);
if (methodRefCandidate instanceof PsiCallExpression) {
final PsiCallExpression callExpression = (PsiCallExpression)methodRefCandidate;
final PsiMethod resolveMethod = callExpression.resolveMethod();
@@ -99,9 +99,11 @@ public class AnonymousCanBeMethodReferenceInspection extends BaseJavaBatchLocalI
final PsiElement lBrace = aClass.getLBrace();
LOG.assertTrue(lBrace != null);
final TextRange rangeInElement = new TextRange(0, aClass.getStartOffsetInParent() + lBrace.getStartOffsetInParent());
ProblemHighlightType highlightType = LambdaCanBeMethodReferenceInspection.checkQualifier(lambdaBodyCandidate) ? ProblemHighlightType.LIKE_UNUSED_SYMBOL
: ProblemHighlightType.INFORMATION;
holder.registerProblem(parent,
"Anonymous #ref #loc can be replaced with method reference",
ProblemHighlightType.LIKE_UNUSED_SYMBOL, rangeInElement, new ReplaceWithMethodRefFix());
highlightType, rangeInElement, new ReplaceWithMethodRefFix());
}
}
}
@@ -129,8 +131,12 @@ public class AnonymousCanBeMethodReferenceInspection extends BaseJavaBatchLocalI
if (methods.length != 1) return;
final PsiParameter[] parameters = methods[0].getParameterList().getParameters();
final String methodRefText = LambdaCanBeMethodReferenceInspection
.convertToMethodReference(methods[0].getBody(), parameters, anonymousClass.getBaseClassType(), anonymousClass.getParent());
final PsiType functionalInterfaceType = anonymousClass.getBaseClassType();
LambdaCanBeMethodReferenceInspection methodReferenceInspection = new LambdaCanBeMethodReferenceInspection();
PsiExpression methodRefCandidate = methodReferenceInspection.extractMethodReferenceCandidateExpression(methods[0].getBody(), false);
final PsiExpression candidate = methodReferenceInspection.canBeMethodReferenceProblem(parameters, functionalInterfaceType, anonymousClass.getParent(), methodRefCandidate);
final String methodRefText = LambdaCanBeMethodReferenceInspection.createMethodReferenceText(candidate, functionalInterfaceType, parameters);
replaceWithMethodReference(project, methodRefText, anonymousClass.getBaseClassType(), anonymousClass.getParent());
}
@@ -17,7 +17,6 @@ package com.intellij.codeInspection;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.codeInspection.dataFlow.ControlFlowAnalyzer;
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
@@ -101,36 +100,36 @@ public class LambdaCanBeMethodReferenceInspection extends BaseJavaBatchLocalInsp
final PsiElement body = expression.getBody();
final PsiType functionalInterfaceType = expression.getFunctionalInterfaceType();
if (functionalInterfaceType != null) {
PsiExpression methodRefCandidate = extractMethodReferenceCandidateExpression(body, false);
final PsiExpression candidate =
canBeMethodReferenceProblem(body, expression.getParameterList().getParameters(), functionalInterfaceType, null);
canBeMethodReferenceProblem(expression.getParameterList().getParameters(), functionalInterfaceType, null, methodRefCandidate);
if (candidate != null) {
ProblemHighlightType errorOrWarning = checkQualifier(methodRefCandidate) ? ProblemHighlightType.GENERIC_ERROR_OR_WARNING
: ProblemHighlightType.INFORMATION;
holder.registerProblem(InspectionProjectProfileManager.isInformationLevel(getShortName(), expression) ? expression : candidate,
"Can be replaced with method reference",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new ReplaceWithMethodRefFix());
errorOrWarning, new ReplaceWithMethodRefFix());
}
}
}
}
};
}
@Nullable
public static String convertToMethodReference(@Nullable final PsiElement body,
final PsiVariable[] parameters,
final PsiType functionalInterfaceType,
@Nullable PsiElement context) {
final PsiExpression candidate = new LambdaCanBeMethodReferenceInspection()
.canBeMethodReferenceProblem(body, parameters, functionalInterfaceType, context);
return createMethodReferenceText(candidate, functionalInterfaceType, parameters);
}
@Nullable
public PsiExpression canBeMethodReferenceProblem(@Nullable final PsiElement body,
final PsiVariable[] parameters,
PsiType functionalInterfaceType,
@Nullable PsiElement context) {
final PsiExpression methodRefCandidate = extractMethodReferenceCandidateExpression(body);
PsiExpression methodRefCandidate = extractMethodReferenceCandidateExpression(body, true);
return canBeMethodReferenceProblem(parameters, functionalInterfaceType, context, methodRefCandidate);
}
@Nullable
public PsiExpression canBeMethodReferenceProblem(final PsiVariable[] parameters,
PsiType functionalInterfaceType,
@Nullable PsiElement context,
final PsiExpression methodRefCandidate) {
if (methodRefCandidate instanceof PsiNewExpression) {
final PsiNewExpression newExpression = (PsiNewExpression)methodRefCandidate;
if (newExpression.getAnonymousClass() != null || newExpression.getArrayInitializer() != null) {
@@ -264,18 +263,18 @@ public class LambdaCanBeMethodReferenceInspection extends BaseJavaBatchLocalInsp
}
@Nullable
private PsiExpression extractMethodReferenceCandidateExpression(PsiElement body) {
public PsiExpression extractMethodReferenceCandidateExpression(PsiElement body, boolean checkSideEffectPureQualifier) {
final PsiExpression expression = LambdaUtil.extractSingleExpressionFromBody(body);
if (expression == null) {
return null;
}
if (expression instanceof PsiNewExpression) {
if (checkQualifier(((PsiNewExpression)expression).getQualifier())) {
if (!checkSideEffectPureQualifier || checkQualifier(((PsiNewExpression)expression).getQualifier())) {
return expression;
}
}
else if (expression instanceof PsiMethodCallExpression) {
if (checkQualifier(((PsiMethodCallExpression)expression).getMethodExpression().getQualifier())) {
if (!checkSideEffectPureQualifier || checkQualifier(((PsiMethodCallExpression)expression).getMethodExpression().getQualifier())) {
return expression;
}
}
@@ -323,17 +322,11 @@ public class LambdaCanBeMethodReferenceInspection extends BaseJavaBatchLocalInsp
return tryConvertToMethodReference(lambda, candidate);
}
private static boolean checkQualifier(PsiElement qualifier) {
public static boolean checkQualifier(PsiElement qualifier) {
if (qualifier == null) {
return true;
}
final Condition<PsiElement> callExpressionCondition = element -> {
if (element instanceof PsiCallExpression) {
PsiMethod method = ((PsiCallExpression)element).resolveMethod();
return !(method != null && ControlFlowAnalyzer.isPure(method));
}
return false;
};
final Condition<PsiElement> callExpressionCondition = Conditions.instanceOf(PsiCallExpression.class);
final Condition<PsiElement> nonFinalFieldRefCondition = expression -> {
if (expression instanceof PsiReferenceExpression) {
PsiElement element = ((PsiReferenceExpression)expression).resolve();
@@ -0,0 +1,13 @@
// "Replace lambda with method reference" "true"
class Example {
public void m() {
}
{
Runnable r = ex()::m;
}
Example ex() {
return this;
}
}
@@ -1,4 +1,4 @@
// "Replace lambda with method reference" "false"
// "Replace lambda with method reference" "true"
class Example {
public void m() {
}