Stream API migration: ignore mutable variables in nested lambdas/anonymous classes; add type argument to map/mapToObj calls where necessary

This commit is contained in:
Tagir Valeev
2016-10-24 11:01:06 +07:00
parent aff7104a99
commit 7620508bc3
7 changed files with 109 additions and 1 deletions
@@ -25,6 +25,7 @@ import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.controlFlow.*;
import com.intellij.psi.impl.PsiDiamondTypeUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.siyeh.ig.psiutils.ExpressionUtils;
import one.util.streamex.StreamEx;
@@ -109,9 +110,39 @@ abstract class MigrateToStreamFix implements LocalQuickFix {
static void simplifyAndFormat(@NotNull Project project, PsiElement result) {
if (result == null) return;
LambdaCanBeMethodReferenceInspection.replaceAllLambdasWithMethodReferences(result);
removeRedundantTypeArguments(project, result);
CodeStyleManager.getInstance(project).reformat(JavaCodeStyleManager.getInstance(project).shortenClassReferences(result));
}
private static void removeRedundantTypeArguments(@NotNull Project project, PsiElement result) {
PsiElement[] typedCalls = PsiTreeUtil.collectElements(result, e -> {
if (!(e instanceof PsiMethodCallExpression)) return false;
PsiMethodCallExpression call = (PsiMethodCallExpression)e;
if (call.getTypeArguments().length == 0) return false;
PsiMethod method = call.resolveMethod();
if (method == null) return false;
PsiClass aClass = method.getContainingClass();
if (aClass == null) return false;
String className = aClass.getQualifiedName();
// We remove only those which related to Stream API calls trying to preserve ones which were originally in code
return className != null && className.startsWith("java.util.stream.");
});
for(PsiElement typedCall : typedCalls) {
PsiMethodCallExpression call = (PsiMethodCallExpression)typedCall;
PsiType[] arguments = call.getTypeArguments();
PsiMethod method = call.resolveMethod();
if(method != null) {
PsiTypeParameter[] parameters = method.getTypeParameters();
if(arguments.length == parameters.length &&
PsiDiamondTypeUtil.areTypeArgumentsRedundant(arguments, call, false, method, parameters)) {
PsiMethodCallExpression expr =
(PsiMethodCallExpression)JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText("foo()", null);
call.getTypeArgumentList().replace(expr.getTypeArgumentList());
}
}
}
}
static void restoreComments(PsiLoopStatement loopStatement, PsiStatement body) {
final PsiElement parent = loopStatement.getParent();
for (PsiElement comment : PsiTreeUtil.findChildrenOfType(body, PsiComment.class)) {
@@ -572,7 +572,9 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
PsiBreakStatement.class, PsiReturnStatement.class, PsiThrowStatement.class);
int startOffset = controlFlow.getStartOffset(body);
int endOffset = controlFlow.getEndOffset(body);
PsiElement surrounder = PsiTreeUtil.getParentOfType(statement, PsiLambdaExpression.class, PsiClass.class);
final List<PsiVariable> nonFinalVariables = StreamEx.of(ControlFlowUtil.getUsedVariables(controlFlow, startOffset, endOffset))
.remove(variable -> PsiTreeUtil.getParentOfType(variable, PsiLambdaExpression.class, PsiClass.class) != surrounder)
.remove(variable -> isVariableSuitableForStream(variable, statement, tb)).toList();
if (exitPoints.isEmpty()) {
@@ -978,6 +980,9 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
operationName = "mapToObj";
}
PsiExpression expression = myType == null ? myExpression : RefactoringUtil.convertInitializerToNormalExpression(myExpression, myType);
if(myType != null && !(myType instanceof PsiPrimitiveType)) {
operationName = "<"+myType.getCanonicalText()+">"+operationName;
}
return "." + operationName + "(" + LambdaUtil.createLambda(myVariable, expression) + ")";
}