redundant casts in lambda return expressions (IDEA-132723)

This commit is contained in:
Anna Kozlova
2014-11-12 13:13:33 +01:00
parent 8a6e0a2a38
commit 89d1df1dc0
3 changed files with 123 additions and 1 deletions
@@ -25,6 +25,7 @@ import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -341,6 +342,47 @@ public class RedundantCastUtil {
}
}
}
else if (arg instanceof PsiLambdaExpression) {
final PsiType interfaceType = ((PsiLambdaExpression)arg).getFunctionalInterfaceType();
if (interfaceType != null) {
List<PsiExpression> expressions = LambdaUtil.getReturnExpressions((PsiLambdaExpression)arg);
for (int returnExprIdx = 0; returnExprIdx < expressions.size(); returnExprIdx++) {
PsiExpression returnExpression = deparenthesizeExpression(expressions.get(returnExprIdx));
if (returnExpression instanceof PsiTypeCastExpression) {
processLambdaReturnExpression(expression, i, interfaceType, (PsiTypeCastExpression)returnExpression, returnExprIdx,
new Function<PsiExpression, PsiTypeCastExpression>() {
@Override
public PsiTypeCastExpression fun(PsiExpression expression) {
return (PsiTypeCastExpression)expression;
}
});
}
else if (returnExpression instanceof PsiConditionalExpression) {
final PsiExpression thenExpression = ((PsiConditionalExpression)returnExpression).getThenExpression();
if (thenExpression instanceof PsiTypeCastExpression) {
processLambdaReturnExpression(expression, i, interfaceType, (PsiTypeCastExpression)thenExpression,
returnExprIdx, new Function<PsiExpression, PsiTypeCastExpression>() {
@Override
public PsiTypeCastExpression fun(PsiExpression expression) {
return (PsiTypeCastExpression)((PsiConditionalExpression)expression).getThenExpression();
}
});
}
final PsiExpression elseExpression = ((PsiConditionalExpression)returnExpression).getElseExpression();
if (elseExpression instanceof PsiTypeCastExpression) {
processLambdaReturnExpression(expression, i, interfaceType, (PsiTypeCastExpression)elseExpression,
returnExprIdx, new Function<PsiExpression, PsiTypeCastExpression>() {
@Override
public PsiTypeCastExpression fun(PsiExpression expression) {
return (PsiTypeCastExpression)((PsiConditionalExpression)expression).getElseExpression();
}
});
}
}
}
}
}
}
}
catch (IncorrectOperationException e) {
@@ -360,6 +402,29 @@ public class RedundantCastUtil {
}
}
private void processLambdaReturnExpression(PsiCall expression,
int i,
PsiType interfaceType,
PsiTypeCastExpression returnExpression,
int returnExprIdx,
Function<PsiExpression, PsiTypeCastExpression> computeCastExpression) {
final PsiCall newCall = (PsiCall)expression.copy();
final PsiExpressionList newArgsList = newCall.getArgumentList();
LOG.assertTrue(newArgsList != null);
final PsiExpression[] newArgs = newArgsList.getExpressions();
final PsiLambdaExpression lambdaExpression = (PsiLambdaExpression)deparenthesizeExpression(newArgs[i]);
LOG.assertTrue(lambdaExpression != null, newCall);
final PsiExpression newReturnExpression = deparenthesizeExpression(LambdaUtil.getReturnExpressions(lambdaExpression).get(returnExprIdx));
PsiTypeCastExpression castExpression = computeCastExpression.fun(newReturnExpression);
PsiExpression castOperand = castExpression.getOperand();
if (castOperand == null) return;
castExpression.replace(castOperand);
final PsiType functionalInterfaceType = lambdaExpression.getFunctionalInterfaceType();
if (interfaceType.equals(functionalInterfaceType)) {
addToResults(returnExpression);
}
}
@Override public void visitTypeCastExpression(PsiTypeCastExpression typeCast) {
PsiExpression operand = typeCast.getOperand();
if (operand == null) return;
@@ -404,7 +469,7 @@ public class RedundantCastUtil {
return;
}
final PsiType functionalInterfaceType = LambdaUtil.getFunctionalInterfaceType(typeCast, true);
final PsiType functionalInterfaceType = PsiTypesUtil.getExpectedTypeByParent(typeCast);
if (topCastType != null && functionalInterfaceType != null && !TypeConversionUtil.isAssignable(topCastType, functionalInterfaceType, false)) return;
}
processAlreadyHasTypeCast(typeCast);
@@ -456,6 +521,15 @@ public class RedundantCastUtil {
if (parent instanceof PsiReturnStatement) return;
if (parent instanceof PsiTypeCastExpression) return;
if (parent instanceof PsiLambdaExpression) return;
if (parent instanceof PsiConditionalExpression) {
PsiElement gParent = PsiUtil.skipParenthesizedExprUp(parent.getParent());
if (gParent instanceof PsiLambdaExpression) return;
if (gParent instanceof PsiReturnStatement &&
PsiTreeUtil.getParentOfType(gParent, PsiMethod.class, PsiLambdaExpression.class) instanceof PsiLambdaExpression) return;
}
if (isTypeCastSemantic(typeCast)) return;
PsiTypeElement typeElement = typeCast.getCastType();
@@ -0,0 +1,40 @@
import java.util.function.Supplier;
class Main {
private static void boom() {}
private static <R> void map (Supplier<R> fn) {}
private static void map1(Supplier<Runnable> fn) {}
public static void main(String[] args) {
Runnable r = () -> {};
map(() -> (Runnable) Main::boom );
map(() -> true ? (Runnable) Main::boom : (Runnable) Main::boom );
map(() -> {
return true ? (Runnable) Main::boom : (Runnable) Main::boom;
});
map(() -> true ? (Runnable) Main::boom : r );
map(() -> (true ? (Runnable)(Main::boom) : r));
map(() -> {
if (true) {
return (Runnable) Main::boom;
}
return (Runnable) Main::boom;
});
map1(() -> (<warning descr="Casting 'Main::boom' to 'Runnable' is redundant">Runnable</warning>) Main::boom);
map1(() -> true ? (<warning descr="Casting 'Main::boom' to 'Runnable' is redundant">Runnable</warning>) Main::boom : (<warning descr="Casting 'Main::boom' to 'Runnable' is redundant">Runnable</warning>) Main::boom);
map1(() -> {
return true ? (<warning descr="Casting 'Main::boom' to 'Runnable' is redundant">Runnable</warning>) Main::boom : (<warning descr="Casting 'Main::boom' to 'Runnable' is redundant">Runnable</warning>) Main::boom;
});
map1(() -> true ? (<warning descr="Casting 'Main::boom' to 'Runnable' is redundant">Runnable</warning>) Main::boom : r);
map1(() -> (true ? (<warning descr="Casting '(Main::boom)' to 'Runnable' is redundant">Runnable</warning>) (Main::boom) : r));
map1(() -> {
if (true) {
return (<warning descr="Casting 'Main::boom' to 'Runnable' is redundant">Runnable</warning>) Main::boom;
}
return (<warning descr="Casting 'Main::boom' to 'Runnable' is redundant">Runnable</warning>) Main::boom;
});
}
}
@@ -18,6 +18,8 @@ package com.intellij.codeInsight.daemon.lambda;
import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.redundantCast.RedundantCastInspection;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.testFramework.IdeaTestUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -34,7 +36,13 @@ public class LambdaRedundantCastTest extends LightDaemonAnalyzerTestCase {
public void testIntersection() { doTest(); }
public void testSer() { doTest(); }
public void testLambdaReturnExpressions() { doTest(); }
private void doTest() {
doTest(BASE_PATH + "/" + getTestName(false) + ".java", true, false);
}
@Override
protected Sdk getProjectJDK() {
return IdeaTestUtil.getMockJdk18();
}
}