anonym -> lambda, method ref: do not suggest replacement when default method is called out of functional interface context

This commit is contained in:
Anna Kozlova
2014-04-08 13:25:01 +02:00
parent e9996cf64c
commit c8bbd66850
8 changed files with 120 additions and 10 deletions

View File

@@ -30,6 +30,7 @@ import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.controlFlow.AnalysisCanceledException;
import com.intellij.psi.controlFlow.ControlFlow;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.Function;
@@ -304,6 +305,17 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
}
}
public static boolean functionalInterfaceMethodReferenced(PsiMethod psiMethod, PsiAnonymousClass anonymClass) {
if (psiMethod != null && !psiMethod.hasModifierProperty(PsiModifier.STATIC)) {
final PsiClass containingClass = psiMethod.getContainingClass();
if (InheritanceUtil.isInheritorOrSelf(anonymClass, containingClass, true) &&
!InheritanceUtil.hasEnclosingInstanceInScope(containingClass, anonymClass.getParent(), true, true)) {
return true;
}
}
return false;
}
private static class ForbiddenRefsChecker extends JavaRecursiveElementWalkingVisitor {
private boolean myBodyContainsForbiddenRefs;
private final Set<PsiLocalVariable> myLocals = ContainerUtilRt.newHashSet(5);
@@ -324,6 +336,7 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
super.visitMethodCallExpression(methodCallExpression);
final PsiMethod psiMethod = methodCallExpression.resolveMethod();
if (psiMethod == myMethod ||
functionalInterfaceMethodReferenced(psiMethod, myAnonymClass) ||
psiMethod != null &&
!methodCallExpression.getMethodExpression().isQualified() &&
"getClass".equals(psiMethod.getName()) &&

View File

@@ -74,16 +74,19 @@ public class AnonymousCanBeMethodReferenceInspection extends BaseJavaBatchLocalI
final PsiCallExpression callExpression =
LambdaCanBeMethodReferenceInspection
.canBeMethodReferenceProblem(body, methods[0].getParameterList().getParameters(), baseClassType);
if (callExpression != null && callExpression.resolveMethod() != methods[0]) {
final PsiElement parent = aClass.getParent();
if (parent instanceof PsiNewExpression) {
final PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)parent).getClassOrAnonymousClassReference();
if (classReference != null) {
final PsiElement lBrace = aClass.getLBrace();
LOG.assertTrue(lBrace != null);
final TextRange rangeInElement = new TextRange(0, aClass.getStartOffsetInParent() + lBrace.getStartOffsetInParent());
holder.registerProblem(parent,
"Anonymous #ref #loc can be replaced with method reference", ProblemHighlightType.LIKE_UNUSED_SYMBOL, rangeInElement, new ReplaceWithMethodRefFix());
if (callExpression != null) {
final PsiMethod resolveMethod = callExpression.resolveMethod();
if (resolveMethod != methods[0] && !AnonymousCanBeLambdaInspection.functionalInterfaceMethodReferenced(resolveMethod, aClass)) {
final PsiElement parent = aClass.getParent();
if (parent instanceof PsiNewExpression) {
final PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)parent).getClassOrAnonymousClassReference();
if (classReference != null) {
final PsiElement lBrace = aClass.getLBrace();
LOG.assertTrue(lBrace != null);
final TextRange rangeInElement = new TextRange(0, aClass.getStartOffsetInParent() + lBrace.getStartOffsetInParent());
holder.registerProblem(parent,
"Anonymous #ref #loc can be replaced with method reference", ProblemHighlightType.LIKE_UNUSED_SYMBOL, rangeInElement, new ReplaceWithMethodRefFix());
}
}
}
}

View File

@@ -0,0 +1,15 @@
// "Replace with lambda" "true"
class Test {
interface InOut {
void run() throws IOException;
default void foo(){}
}
interface InOutEx extends InOut {
InOut bind() {
return () -> {
foo();
};
}
}
}

View File

@@ -0,0 +1,13 @@
// "Replace with lambda" "true"
class Test {
interface InOut {
void run() throws IOException;
static void foo(){}
}
InOut bind() {
return () -> {
InOut.foo();
};
}
}

View File

@@ -0,0 +1,16 @@
// "Replace with lambda" "false"
class Test {
interface InOut {
void run() throws IOException;
default void foo(){}
}
InOut bind() {
return new In<caret>Out() {
@Override
public void run() throws IOException {
foo();
}
};
}
}

View File

@@ -0,0 +1,18 @@
// "Replace with lambda" "true"
class Test {
interface InOut {
void run() throws IOException;
default void foo(){}
}
interface InOutEx extends InOut {
InOut bind() {
return new In<caret>Out() {
@Override
public void run() throws IOException {
foo();
}
};
}
}
}

View File

@@ -0,0 +1,16 @@
// "Replace with lambda" "true"
class Test {
interface InOut {
void run() throws IOException;
static void foo(){}
}
InOut bind() {
return new In<caret>Out() {
@Override
public void run() throws IOException {
foo();
}
};
}
}

View File

@@ -0,0 +1,16 @@
// "Replace with method reference" "false"
class Test {
interface InOut {
void run() throws IOException;
default void foo(){}
}
InOut bind() {
return new In<caret>Out() {
@Override
public void run() throws IOException {
foo();
}
};
}
}