java redundant cast: cleanup: extract foreach, instanceof, throws statements

GitOrigin-RevId: ac04ea8db453518f4c15d3e37e33d9a25a22ce35
This commit is contained in:
Anna Kozlova
2020-05-06 13:11:34 +02:00
committed by intellij-monorepo-bot
parent c6f03bad18
commit 1c35803b4a
6 changed files with 148 additions and 54 deletions

View File

@@ -167,15 +167,7 @@ public class RedundantCastUtil {
if (rExpr instanceof PsiTypeCastExpression) {
PsiExpression castOperand = deparenthesizeExpression(((PsiTypeCastExpression)rExpr).getOperand());
if (castOperand != null) {
PsiType operandType;
if (castOperand instanceof PsiTypeCastExpression) {
PsiExpression nestedCastOperand = ((PsiTypeCastExpression)castOperand).getOperand();
while (nestedCastOperand instanceof PsiTypeCastExpression) {
nestedCastOperand = deparenthesizeExpression(((PsiTypeCastExpression)nestedCastOperand).getOperand());
}
operandType = nestedCastOperand != null ? nestedCastOperand.getType() : null;
}
else if (castOperand instanceof PsiFunctionalExpression && lType != null) {
if (castOperand instanceof PsiFunctionalExpression && lType != null) {
final PsiTypeElement typeElement = ((PsiTypeCastExpression)rExpr).getCastType();
final PsiType castType = typeElement != null ? typeElement.getType() : null;
if (lType.equals(castType)) {
@@ -183,9 +175,7 @@ public class RedundantCastUtil {
}
return;
}
else {
operandType = castOperand.getType();
}
PsiType operandType = getNestedCastOperandType(castOperand);
if (operandType != null) {
if (lType != null && TypeConversionUtil.isAssignable(lType, operandType, false)) {
addToResults((PsiTypeCastExpression)rExpr);
@@ -194,6 +184,22 @@ public class RedundantCastUtil {
}
}
}
private static PsiType getNestedCastOperandType(PsiExpression castOperand) {
if (castOperand instanceof PsiTypeCastExpression) {
PsiExpression nestedCastOperand = ((PsiTypeCastExpression)castOperand).getOperand();
while (nestedCastOperand instanceof PsiTypeCastExpression) {
nestedCastOperand = deparenthesizeExpression(((PsiTypeCastExpression)nestedCastOperand).getOperand());
}
return nestedCastOperand != null ? nestedCastOperand.getType() : null;
}
else if (castOperand instanceof PsiFunctionalExpression) {
return null;
}
else {
return castOperand.getType();
}
}
@Override
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
@@ -565,12 +571,95 @@ public class RedundantCastUtil {
return true;
}
@Override
public void visitForeachStatement(PsiForeachStatement statement) {
PsiExpression iteratedValue = deparenthesizeExpression(statement.getIteratedValue());
if (iteratedValue instanceof PsiTypeCastExpression) {
PsiExpression operand = ((PsiTypeCastExpression)iteratedValue).getOperand();
if (operand != null) {
PsiType collectionItemType = JavaGenericsUtil.getCollectionItemType(operand.getType(), statement.getResolveScope());
if (collectionItemType != null && TypeConversionUtil.isAssignable(statement.getIterationParameter().getType(), collectionItemType)) {
addToResults((PsiTypeCastExpression)iteratedValue);
}
}
}
super.visitForeachStatement(statement);
}
@Override
public void visitInstanceOfExpression(PsiInstanceOfExpression expression) {
final PsiTypeElement checkTypeElement = expression.getCheckType();
if (checkTypeElement == null) return;
PsiExpression typeCast = deparenthesizeExpression(expression.getOperand());
if (typeCast instanceof PsiTypeCastExpression) {
PsiExpression operand = ((PsiTypeCastExpression)typeCast).getOperand();
if (operand != null) {
PsiType opType = getNestedCastOperandType(operand);
//15.20.2. Type Comparison Operator instanceof:
//If a cast (p15.16) of the RelationalExpression to the ReferenceType would be rejected as a compile-time error,
//then the instanceof relational expression likewise produces a compile-time error.
if (opType != null &&
!(opType instanceof PsiPrimitiveType) &&
TypeConversionUtil.areTypesConvertible(opType, checkTypeElement.getType())) {
addToResults((PsiTypeCastExpression)typeCast);
}
}
}
super.visitInstanceOfExpression(expression);
}
@Override
public void visitThrowStatement(PsiThrowStatement statement) {
PsiExpression typeCast = deparenthesizeExpression(statement.getException());
if (typeCast instanceof PsiTypeCastExpression) {
PsiExpression operand = ((PsiTypeCastExpression)typeCast).getOperand();
if (operand != null) {
PsiType opType = getNestedCastOperandType(operand);
final PsiClass thrownClass = PsiUtil.resolveClassInType(opType);
if (InheritanceUtil.isInheritor(thrownClass, false, CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION)) {
addToResults((PsiTypeCastExpression)typeCast);
}
if (InheritanceUtil.isInheritor(thrownClass, false, CommonClassNames.JAVA_LANG_THROWABLE)) {
final PsiParameterListOwner listOwner = PsiTreeUtil.getParentOfType(statement, PsiMethod.class, PsiLambdaExpression.class);
if (listOwner instanceof PsiMethod) {
processThrowsList((PsiMethod)listOwner, PsiSubstitutor.EMPTY, (PsiTypeCastExpression)typeCast, opType);
}
else if (listOwner instanceof PsiLambdaExpression) {
PsiType functionalInterfaceType = ((PsiLambdaExpression)listOwner).getFunctionalInterfaceType();
final PsiClassType.ClassResolveResult functionalInterfaceResolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
if (interfaceMethod != null) {
processThrowsList(interfaceMethod, LambdaUtil.getSubstitutor(interfaceMethod, functionalInterfaceResolveResult), (PsiTypeCastExpression)typeCast, opType);
}
}
}
}
}
super.visitThrowStatement(statement);
}
private void processThrowsList(PsiMethod interfaceMethod,
PsiSubstitutor psiSubstitutor,
PsiTypeCastExpression typeCast,
PsiType opType) {
for (PsiClassType thrownType : interfaceMethod.getThrowsList().getReferencedTypes()) {
PsiType left = psiSubstitutor.substitute(thrownType);
if (left != null && TypeConversionUtil.isAssignable(left, opType, false)) {
addToResults(typeCast);
}
}
}
private void processAlreadyHasTypeCast(PsiTypeCastExpression typeCast){
PsiElement parent = PsiUtil.skipParenthesizedExprUp(typeCast.getParent());
if (parent instanceof PsiExpressionList) return; // do not replace in arg lists - should be handled by parent
if (parent instanceof PsiReturnStatement) return;
if (parent instanceof PsiTypeCastExpression) return;
if (parent instanceof PsiPolyadicExpression) return;
if (parent instanceof PsiForeachStatement) return;
if (parent instanceof PsiInstanceOfExpression) return;
if (parent instanceof PsiThrowStatement) return;
if (parent instanceof PsiLambdaExpression) return;
@@ -680,43 +769,6 @@ public class RedundantCastUtil {
}
}
if (parent instanceof PsiForeachStatement) {
PsiType collectionItemType = JavaGenericsUtil.getCollectionItemType(opType, typeCast.getResolveScope());
if (collectionItemType != null && TypeConversionUtil.isAssignable(((PsiForeachStatement)parent).getIterationParameter().getType(), collectionItemType)) {
addToResults(typeCast);
}
return;
}
if (parent instanceof PsiThrowStatement) {
final PsiClass thrownClass = PsiUtil.resolveClassInType(opType);
if (InheritanceUtil.isInheritor(thrownClass, false, CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION)) {
addToResults(typeCast);
return;
}
if (InheritanceUtil.isInheritor(thrownClass, false, CommonClassNames.JAVA_LANG_THROWABLE)) {
final PsiMethod method = PsiTreeUtil.getParentOfType(parent, PsiMethod.class);//todo lambda
if (method != null) {
for (PsiClassType thrownType : method.getThrowsList().getReferencedTypes()) {
if (TypeConversionUtil.isAssignable(thrownType, opType, false)) {
addToResults(typeCast);
return;
}
}
}
}
}
if (parent instanceof PsiInstanceOfExpression) {
if (opType instanceof PsiPrimitiveType) return;
//15.20.2. Type Comparison Operator instanceof:
//If a cast (p15.16) of the RelationalExpression to the ReferenceType would be rejected as a compile-time error,
//then the instanceof relational expression likewise produces a compile-time error.
final PsiTypeElement checkTypeElement = ((PsiInstanceOfExpression)parent).getCheckType();
if (checkTypeElement != null && TypeConversionUtil.areTypesConvertible(opType, checkTypeElement.getType())) {
addToResults(typeCast);
return;
}
}
if (parent instanceof PsiSwitchBlock &&
opType instanceof PsiClassType && PsiPrimitiveType.getUnboxedType(opType) == null && !opType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
PsiClass aClass = ((PsiClassType)opType).resolve();
@@ -825,9 +877,8 @@ public class RedundantCastUtil {
if (isCastToSerializable(castType)) return true;
}
else if (stripParenthesisOperand instanceof PsiConditionalExpression) {
final PsiExpression thenExpr = PsiUtil.skipParenthesizedExprDown(((PsiConditionalExpression)stripParenthesisOperand).getThenExpression());
final PsiExpression elseExpr = PsiUtil.skipParenthesizedExprDown(((PsiConditionalExpression)stripParenthesisOperand).getElseExpression());
if (thenExpr instanceof PsiFunctionalExpression || elseExpr instanceof PsiFunctionalExpression) {
if (PsiUtil.skipParenthesizedExprDown(((PsiConditionalExpression)stripParenthesisOperand).getThenExpression()) instanceof PsiFunctionalExpression ||
PsiUtil.skipParenthesizedExprDown(((PsiConditionalExpression)stripParenthesisOperand).getElseExpression()) instanceof PsiFunctionalExpression) {
return true;
}
}

View File

@@ -0,0 +1,16 @@
interface SAM<T, R extends Throwable> {
void m(T t) throws R;
}
class MyTest {
void f(E1 e1) {
SAM<String, E1> s1 = s -> {
if (s.length() > 0) throw (<warning descr="Casting '(E2)e1' to 'E1' is redundant">E1</warning>) (<warning descr="Casting 'e1' to 'E2' is redundant">E2</warning>) e1;
throw (<warning descr="Casting '(E1)e1' to 'E2' is redundant">E2</warning>) (<warning descr="Casting 'e1' to 'E1' is redundant">E1</warning>)e1;
};
}
static class E1 extends Exception {}
static class E2 extends E1 {}
}

View File

@@ -3,11 +3,34 @@ public class RedundantCast{
throw <error descr="Inconvertible types; cannot cast 'RedundantCast.IOEx' to 'RedundantCast.FileNotFoundEx'">(<warning descr="Casting 'e' to 'FileNotFoundEx' is redundant">FileNotFoundEx</warning>)e</error>;
}
void foo(RuntimeException e) {
throw (<warning descr="Casting 'e' to 'Ex' is redundant">Ex</warning>)e;
void foo(RuntimeException e, boolean f) {
if (f) {
throw (<warning descr="Casting '(Object)e' to 'Ex' is redundant">Ex</warning>)(<warning descr="Casting 'e' to 'Object' is redundant">Object</warning>)e;
}
else if (!f && false) {
throw <error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.Throwable'">(<warning descr="Casting 'e' to 'Object' is redundant">Object</warning>)e</error>;
}
throw (<warning descr="Casting '(Ex)e' to 'Ex' is redundant">Ex</warning>)(<warning descr="Casting 'e' to 'Ex' is redundant">Ex</warning>)e;
}
void bar(E2 e) throws E1 {
throw (<warning descr="Casting 'e' to 'E1' is redundant">E1</warning>)e;
}
void bar(E1 e) throws E2 {
if (true) {
try {
throw (E2)e;
}
catch (E2 e2) {}
}
throw (E2)e;
}
static class Ex extends RuntimeException {}
static class IOEx extends Exception {}
static class FileNotFoundEx extends Exception {}
static class E1 extends Exception {}
static class E2 extends E1 {}
}

View File

@@ -5,8 +5,10 @@ class RedundantCast {
void redundantCasts() {
List<String> list = new ArrayList<>();
for (String s : (<warning descr="Casting 'list' to 'ArrayList<String>' is redundant">ArrayList<String></warning>) list) {}
for (String s : (<warning descr="Casting '(ArrayList<String>)list' to 'ArrayList<String>' is redundant">ArrayList<String></warning>) (<warning descr="Casting 'list' to 'ArrayList<String>' is redundant">ArrayList<String></warning>) list) {}
Object o = new ArrayList<>();
for (String s : (ArrayList<String>) o) {}
for (String s : (ArrayList<String>) (<warning descr="Casting 'o' to 'Object' is redundant">Object</warning>) o) {}
}
}

View File

@@ -5,6 +5,7 @@ class RedundantCast {
boolean redundantCasts(Object o) {
int p = 0;
if ((Number)p instanceof Integer) {}
if ((Number)(<warning descr="Casting 'p' to 'Object' is redundant">Object</warning>)p instanceof Integer) {}
return (<warning descr="Casting 'o' to 'List' is redundant">List</warning>)o instanceof ArrayList;
}
}

View File

@@ -85,6 +85,7 @@ public class LambdaRedundantCastTest extends LightDaemonAnalyzerTestCase {
public void testLambdaWithCastInReturnStatement() { doTest(); }
public void testCastInNeighbourArgument() { doTest(); }
public void testErasedTargetType() { doTest(); }
public void testThrowsStatementInLambdaBody() { doTest(); }
public void testRejectReturnTypeChange() {
doTest();
}