anonymous -> lambda: deep unique names (IDEA-154751)

This commit is contained in:
Anna.Kozlova
2016-04-14 20:07:38 +02:00
parent 4c47e35b31
commit f7137a044d
3 changed files with 75 additions and 43 deletions

View File

@@ -25,7 +25,6 @@ import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
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;
@@ -37,10 +36,9 @@ import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil;
import com.intellij.psi.util.*;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.ContainerUtilRt;
import com.intellij.util.containers.hash.LinkedHashMap;
import com.intellij.util.text.UniqueNameGenerator;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -239,12 +237,7 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
final PsiMethod method;
if (ignoreEqualsMethod) {
final List<PsiMethod> methods = ContainerUtil.filter(anonymousClass.getMethods(), new Condition<PsiMethod>() {
@Override
public boolean value(PsiMethod method) {
return !"equals".equals(method.getName());
}
});
final List<PsiMethod> methods = ContainerUtil.filter(anonymousClass.getMethods(), method1 -> !"equals".equals(method1.getName()));
method = methods.get(0);
} else {
method = anonymousClass.getMethods()[0];
@@ -257,22 +250,9 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
final ForbiddenRefsChecker checker = new ForbiddenRefsChecker(method, anonymousClass);
body.accept(checker);
final PsiResolveHelper helper = PsiResolveHelper.SERVICE.getInstance(body.getProject());
final Set<PsiVariable> conflictingLocals = checker.getLocals();
for (Iterator<PsiVariable> iterator = conflictingLocals.iterator(); iterator.hasNext(); ) {
PsiVariable local = iterator.next();
final String localName = local.getName();
if (localName == null || helper.resolveReferencedVariable(localName, anonymousClass) == null) {
iterator.remove();
}
}
final Project project = element.getProject();
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
ReplaceWithLambdaFix
.giveUniqueNames(project, elementFactory, body, conflictingLocals.toArray(new PsiVariable[conflictingLocals.size()]));
final String withoutTypesDeclared = ReplaceWithLambdaFix.composeLambdaText(method);
PsiLambdaExpression lambdaExpression =
@@ -281,12 +261,18 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
PsiElement lambdaBody = lambdaExpression.getBody();
LOG.assertTrue(lambdaBody != null);
lambdaBody.replace(body);
ReplaceWithLambdaFix
.giveUniqueNames(project, elementFactory, lambdaExpression, lambdaExpression.getParameterList().getParameters());
final PsiNewExpression newExpression = (PsiNewExpression)anonymousClass.getParent();
lambdaExpression = (PsiLambdaExpression)newExpression.replace(lambdaExpression);
final Set<PsiVariable> variables = new HashSet<>();
final Set<String> usedLocalNames = new HashSet<>();
collectLocalVariablesDefinedInsideLambda(lambdaExpression, variables, usedLocalNames);
ReplaceWithLambdaFix
.giveUniqueNames(project, elementFactory, lambdaExpression,
usedLocalNames, variables.toArray(new PsiVariable[variables.size()]));
final PsiExpression singleExpr = RedundantLambdaCodeBlockInspection.isCodeBlockRedundant(lambdaExpression,
lambdaExpression.getBody());
if (singleExpr != null) {
@@ -320,6 +306,37 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
return null;
}
private static void collectLocalVariablesDefinedInsideLambda(PsiLambdaExpression lambdaExpression,
final Set<PsiVariable> variables,
Set<String> namesOfVariablesInTheBlock) {
PsiElement block = PsiUtil.getTopLevelEnclosingCodeBlock(lambdaExpression, null);
if (block == null) {
block = lambdaExpression;
}
block.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitVariable(PsiVariable variable) {
super.visitVariable(variable);
if (!(variable instanceof PsiField)) {
variables.add(variable);
}
}
});
final PsiResolveHelper helper = PsiResolveHelper.SERVICE.getInstance(lambdaExpression.getProject());
for (Iterator<PsiVariable> iterator = variables.iterator(); iterator.hasNext(); ) {
PsiVariable local = iterator.next();
final String localName = local.getName();
if (localName == null ||
helper.resolveReferencedVariable(localName, lambdaExpression) == null ||
!PsiTreeUtil.isAncestor(lambdaExpression, local, false)) {
iterator.remove();
namesOfVariablesInTheBlock.add(localName);
}
}
}
private static class ReplaceWithLambdaFix implements LocalQuickFix, HighPriorityAction {
@NotNull
@Override
@@ -344,17 +361,19 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
private static void giveUniqueNames(Project project,
final PsiElementFactory elementFactory,
PsiElement body,
PsiVariable[] parameters) {
Set<String> usedLocalNames, PsiVariable[] parameters) {
final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
final Map<PsiVariable, String> names = new HashMap<PsiVariable, String>();
for (PsiVariable parameter : parameters) {
String parameterName = parameter.getName();
final String uniqueVariableName = codeStyleManager.suggestUniqueVariableName(parameterName, parameter.getParent(), false);
String uniqueVariableName = UniqueNameGenerator.generateUniqueName(codeStyleManager.suggestUniqueVariableName(parameterName, parameter.getParent(), false), usedLocalNames);
if (!Comparing.equal(parameterName, uniqueVariableName)) {
names.put(parameter, uniqueVariableName);
}
}
if (names.isEmpty()) return;
final LinkedHashMap<PsiElement, PsiElement> replacements = new LinkedHashMap<PsiElement, PsiElement>();
body.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
@@ -390,13 +409,7 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
if (parameters.length != 1) {
buf.append("(");
}
buf.append(StringUtil.join(parameters,
new Function<PsiParameter, String>() {
@Override
public String fun(PsiParameter parameter) {
return composeParameter(parameter);
}
}, ","));
buf.append(StringUtil.join(parameters, ReplaceWithLambdaFix::composeParameter, ","));
if (parameters.length != 1) {
buf.append(")");
}
@@ -437,7 +450,6 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
private static class ForbiddenRefsChecker extends JavaRecursiveElementWalkingVisitor {
private boolean myBodyContainsForbiddenRefs;
private final Set<PsiVariable> myLocals = ContainerUtilRt.newHashSet(5);
private final PsiMethod myMethod;
private final PsiAnonymousClass myAnonymClass;
@@ -492,9 +504,6 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
if (myBodyContainsForbiddenRefs) return;
super.visitVariable(variable);
if (!(variable instanceof PsiField)) {
myLocals.add(variable);
}
}
@Override
@@ -582,9 +591,5 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection
public boolean hasForbiddenRefs() {
return myBodyContainsForbiddenRefs;
}
public Set<PsiVariable> getLocals() {
return myLocals;
}
}
}

View File

@@ -0,0 +1,12 @@
// "Replace with lambda" "true"
import java.util.function.Consumer;
class A {
void anonymousToLambda(String s) {
String s12 = "";
Consumer<String> consumer = s13 -> {
String s1 = "";
};
}
}

View File

@@ -0,0 +1,15 @@
// "Replace with lambda" "true"
import java.util.function.Consumer;
class A {
void anonymousToLambda(String s) {
String s12 = "";
Consumer<String> consumer = new Consu<caret>mer<String>() {
@Override
public void accept(final String s) {
String s1 = "";
}
};
}
}