forbid anonym -> lambda/meth ref if method is synchronized (IDEA-133947)

This commit is contained in:
Anna Kozlova
2014-12-05 20:19:15 +01:00
parent 849936da07
commit e90b046aa4
3 changed files with 69 additions and 43 deletions

View File

@@ -24,6 +24,7 @@ import com.intellij.codeInsight.intention.HighPriorityAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.LanguageLevel;
@@ -85,27 +86,23 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new JavaElementVisitor() {
@Override
public void visitAnonymousClass(PsiAnonymousClass aClass) {
public void visitAnonymousClass(final PsiAnonymousClass aClass) {
super.visitAnonymousClass(aClass);
if (PsiUtil.getLanguageLevel(aClass).isAtLeast(LanguageLevel.JDK_1_8)) {
final PsiClassType baseClassType = aClass.getBaseClassType();
if (LambdaHighlightingUtil.checkInterfaceFunctional(baseClassType) == null) {
final PsiElement lambdaContext = aClass.getParent().getParent();
if (LambdaUtil.isValidLambdaContext(lambdaContext) || !(lambdaContext instanceof PsiExpressionStatement)) {
final PsiMethod[] methods = aClass.getMethods();
if (methods.length == 1 && aClass.getFields().length == 0) {
final PsiMethod psiMethod = methods[0];
final PsiCodeBlock body = psiMethod.getBody();
if (body != null && !hasForbiddenRefsInsideBody(psiMethod, aClass) && !hasRuntimeAnnotations(psiMethod)) {
final PsiElement lBrace = aClass.getLBrace();
LOG.assertTrue(lBrace != null);
final TextRange rangeInElement = new TextRange(0, aClass.getStartOffsetInParent() + lBrace.getStartOffsetInParent());
holder.registerProblem(aClass.getParent(), "Anonymous #ref #loc can be replaced with lambda",
ProblemHighlightType.LIKE_UNUSED_SYMBOL, rangeInElement, new ReplaceWithLambdaFix());
}
final PsiElement parent = aClass.getParent();
final PsiElement lambdaContext = parent != null ? parent.getParent() : null;
if (lambdaContext != null &&
(LambdaUtil.isValidLambdaContext(lambdaContext) || !(lambdaContext instanceof PsiExpressionStatement)) &&
canBeConvertedToLambda(aClass, new Condition<PsiClassType>() {
@Override
public boolean value(PsiClassType type) {
return LambdaHighlightingUtil.checkInterfaceFunctional(type) == 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 lambda",
ProblemHighlightType.LIKE_UNUSED_SYMBOL, rangeInElement, new ReplaceWithLambdaFix());
}
}
};
@@ -192,6 +189,20 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
return null;
}
public static boolean canBeConvertedToLambda(PsiAnonymousClass aClass, Condition<PsiClassType> baseClassTypeCondition) {
if (PsiUtil.getLanguageLevel(aClass).isAtLeast(LanguageLevel.JDK_1_8) && baseClassTypeCondition.value(aClass.getBaseClassType())) {
final PsiMethod[] methods = aClass.getMethods();
if (methods.length == 1 && aClass.getFields().length == 0) {
final PsiMethod method = methods[0];
return method.getBody() != null &&
!hasForbiddenRefsInsideBody(method, aClass) &&
!hasRuntimeAnnotations(method) &&
!method.hasModifierProperty(PsiModifier.SYNCHRONIZED);
}
}
return false;
}
private static class ReplaceWithLambdaFix implements LocalQuickFix, HighPriorityAction {
@NotNull
@Override

View File

@@ -19,6 +19,7 @@ import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.TextRange;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
@@ -66,30 +67,30 @@ public class AnonymousCanBeMethodReferenceInspection extends BaseJavaBatchLocalI
@Override
public void visitAnonymousClass(PsiAnonymousClass aClass) {
super.visitAnonymousClass(aClass);
if (PsiUtil.getLanguageLevel(aClass).isAtLeast(LanguageLevel.JDK_1_8)) {
final PsiClassType baseClassType = aClass.getBaseClassType();
if (LambdaUtil.isFunctionalType(baseClassType)) {
final PsiMethod[] methods = aClass.getMethods();
if (methods.length == 1 && methods[0].getBody() != null && aClass.getFields().length == 0 &&
!AnonymousCanBeLambdaInspection.hasForbiddenRefsInsideBody(methods[0], aClass)) {
final PsiCodeBlock body = methods[0].getBody();
final PsiCallExpression callExpression =
LambdaCanBeMethodReferenceInspection
.canBeMethodReferenceProblem(body, methods[0].getParameterList().getParameters(), baseClassType);
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());
}
}
if (AnonymousCanBeLambdaInspection.canBeConvertedToLambda(aClass, new Condition<PsiClassType>() {
@Override
public boolean value(PsiClassType type) {
return LambdaUtil.isFunctionalType(type);
}
})) {
final PsiMethod method = aClass.getMethods()[0];
final PsiCodeBlock body = method.getBody();
final PsiCallExpression callExpression =
LambdaCanBeMethodReferenceInspection.canBeMethodReferenceProblem(body, method.getParameterList().getParameters(), aClass.getBaseClassType());
if (callExpression != null) {
final PsiMethod resolveMethod = callExpression.resolveMethod();
if (resolveMethod != method &&
!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,14 @@
// "Replace with lambda" "false"
class Test {
interface I {
void m();
}
{
I i = new I<caret>() {
public synchronized void m() {
//do smth
}
}
}
}