blob: ccb606d51376ddf5bb9d471e56d5b20e440fa3cf [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.util;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
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.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author max
* Date: Mar 24, 2002
*/
public class RedundantCastUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.redundantCast.RedundantCastUtil");
private RedundantCastUtil() { }
@NotNull
public static List<PsiTypeCastExpression> getRedundantCastsInside(PsiElement where) {
MyCollectingVisitor visitor = new MyCollectingVisitor();
if (where instanceof PsiEnumConstant) {
where.accept(visitor);
} else {
where.acceptChildren(visitor);
}
return new ArrayList<PsiTypeCastExpression>(visitor.myFoundCasts);
}
public static boolean isCastRedundant (PsiTypeCastExpression typeCast) {
PsiElement parent = typeCast.getParent();
while(parent instanceof PsiParenthesizedExpression) parent = parent.getParent();
if (parent instanceof PsiExpressionList) parent = parent.getParent();
if (parent instanceof PsiReferenceExpression) parent = parent.getParent();
MyIsRedundantVisitor visitor = new MyIsRedundantVisitor(false);
parent.accept(visitor);
return visitor.isRedundant;
}
@Nullable
private static PsiExpression deparenthesizeExpression(PsiExpression arg) {
while (arg instanceof PsiParenthesizedExpression) arg = ((PsiParenthesizedExpression) arg).getExpression();
return arg;
}
public static void removeCast(PsiTypeCastExpression castExpression) {
if (castExpression == null) return;
PsiExpression operand = castExpression.getOperand();
if (operand instanceof PsiParenthesizedExpression) {
final PsiParenthesizedExpression parExpr = (PsiParenthesizedExpression)operand;
operand = parExpr.getExpression();
}
if (operand == null) return;
PsiElement toBeReplaced = castExpression;
PsiElement parent = castExpression.getParent();
while (parent instanceof PsiParenthesizedExpression) {
toBeReplaced = parent;
parent = parent.getParent();
}
try {
toBeReplaced.replace(operand);
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
private static class MyCollectingVisitor extends MyIsRedundantVisitor {
private final Set<PsiTypeCastExpression> myFoundCasts = new HashSet<PsiTypeCastExpression>();
private MyCollectingVisitor() {
super(true);
}
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {
expression.acceptChildren(this);
}
@Override public void visitClass(PsiClass aClass) {
// avoid multiple visit
}
@Override public void visitMethod(PsiMethod method) {
// avoid multiple visit
}
@Override public void visitField(PsiField field) {
// avoid multiple visit
}
@Override
protected void addToResults(@NotNull PsiTypeCastExpression typeCast){
if (!isTypeCastSemantic(typeCast)) {
myFoundCasts.add(typeCast);
}
}
}
private static class MyIsRedundantVisitor extends JavaRecursiveElementVisitor {
private boolean isRedundant = false;
private final boolean myRecursive;
private MyIsRedundantVisitor(final boolean recursive) {
myRecursive = recursive;
}
@Override
public void visitElement(final PsiElement element) {
if (myRecursive) {
super.visitElement(element);
}
}
protected void addToResults(@NotNull PsiTypeCastExpression typeCast){
if (!isTypeCastSemantic(typeCast)) {
isRedundant = true;
}
}
@Override public void visitAssignmentExpression(PsiAssignmentExpression expression) {
processPossibleTypeCast(expression.getRExpression(), expression.getLExpression().getType());
super.visitAssignmentExpression(expression);
}
@Override public void visitVariable(PsiVariable variable) {
processPossibleTypeCast(variable.getInitializer(), variable.getType());
super.visitVariable(variable);
}
@Override public void visitReturnStatement(PsiReturnStatement statement) {
final PsiMethod method = PsiTreeUtil.getParentOfType(statement, PsiMethod.class);
if (method != null) {
final PsiType returnType = method.getReturnType();
final PsiExpression returnValue = statement.getReturnValue();
if (returnValue != null) {
processPossibleTypeCast(returnValue, returnType);
}
}
super.visitReturnStatement(statement);
}
@Override
public void visitPolyadicExpression(PsiPolyadicExpression expression) {
IElementType tokenType = expression.getOperationTokenType();
PsiExpression[] operands = expression.getOperands();
if (operands.length >= 2) {
PsiType lType = operands[0].getType();
processBinaryExpressionOperand(deparenthesizeExpression(operands[0]), operands[1].getType(), tokenType);
for (int i = 1; i < operands.length; i++) {
PsiExpression operand = deparenthesizeExpression(operands[i]);
if (operand == null) continue;
processBinaryExpressionOperand(operand, lType, tokenType);
lType = TypeConversionUtil.calcTypeForBinaryExpression(lType, operand.getType(), tokenType, true);
}
}
super.visitPolyadicExpression(expression);
}
private void processBinaryExpressionOperand(final PsiExpression operand,
final PsiType otherType,
final IElementType binaryToken) {
if (operand instanceof PsiTypeCastExpression) {
PsiTypeCastExpression typeCast = (PsiTypeCastExpression)operand;
PsiExpression toCast = typeCast.getOperand();
if (toCast != null && TypeConversionUtil.isBinaryOperatorApplicable(binaryToken, toCast.getType(), otherType, false)) {
addToResults(typeCast);
}
}
}
private void processPossibleTypeCast(PsiExpression rExpr, @Nullable PsiType lType) {
rExpr = deparenthesizeExpression(rExpr);
if (rExpr instanceof PsiTypeCastExpression) {
PsiExpression castOperand = ((PsiTypeCastExpression)rExpr).getOperand();
if (castOperand != null) {
PsiType operandType = castOperand.getType();
if (operandType != null) {
if (lType != null && TypeConversionUtil.isAssignable(lType, operandType, false)) {
addToResults((PsiTypeCastExpression)rExpr);
}
}
}
}
}
@Override public void visitMethodCallExpression(PsiMethodCallExpression expression) {
processCall(expression);
checkForVirtual(expression);
super.visitMethodCallExpression(expression);
}
private void checkForVirtual(PsiMethodCallExpression methodCall) {
PsiReferenceExpression methodExpr = methodCall.getMethodExpression();
PsiExpression qualifier = methodExpr.getQualifierExpression();
if (!(qualifier instanceof PsiParenthesizedExpression)) return;
PsiExpression operand = ((PsiParenthesizedExpression)qualifier).getExpression();
if (!(operand instanceof PsiTypeCastExpression)) return;
PsiTypeCastExpression typeCast = (PsiTypeCastExpression)operand;
PsiExpression castOperand = typeCast.getOperand();
if (castOperand == null) return;
PsiType type = castOperand.getType();
if (type == null) return;
if (type instanceof PsiPrimitiveType) return;
final JavaResolveResult resolveResult = methodExpr.advancedResolve(false);
PsiMethod targetMethod = (PsiMethod)resolveResult.getElement();
if (targetMethod == null) return;
if (targetMethod.hasModifierProperty(PsiModifier.STATIC)) return;
try {
PsiManager manager = methodExpr.getManager();
PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
final PsiExpression expressionFromText = factory.createExpressionFromText(methodCall.getText(), methodCall);
if (!(expressionFromText instanceof PsiMethodCallExpression)) return;
PsiMethodCallExpression newCall = (PsiMethodCallExpression)expressionFromText;
PsiExpression newQualifier = newCall.getMethodExpression().getQualifierExpression();
PsiExpression newOperand = ((PsiTypeCastExpression)((PsiParenthesizedExpression)newQualifier).getExpression()).getOperand();
newQualifier.replace(newOperand);
final JavaResolveResult newResult = newCall.getMethodExpression().advancedResolve(false);
if (!newResult.isValidResult()) return;
final PsiMethod newTargetMethod = (PsiMethod)newResult.getElement();
final PsiType newReturnType = newCall.getType();
final PsiType oldReturnType = methodCall.getType();
if (Comparing.equal(newReturnType, oldReturnType)) {
if (newTargetMethod.equals(targetMethod) ||
(newTargetMethod.getSignature(newResult.getSubstitutor()).equals(targetMethod.getSignature(resolveResult.getSubstitutor())) &&
!(newTargetMethod.isDeprecated() && !targetMethod.isDeprecated()) && // see SCR11555, SCR14559
areThrownExceptionsCompatible(targetMethod, newTargetMethod))) {
addToResults(typeCast);
}
}
}
catch (IncorrectOperationException ignore) { }
}
private static boolean areThrownExceptionsCompatible(final PsiMethod targetMethod, final PsiMethod newTargetMethod) {
final PsiClassType[] oldThrowsTypes = targetMethod.getThrowsList().getReferencedTypes();
final PsiClassType[] newThrowsTypes = newTargetMethod.getThrowsList().getReferencedTypes();
for (final PsiClassType throwsType : newThrowsTypes) {
if (!isExceptionThrown(throwsType, oldThrowsTypes)) return false;
}
return true;
}
private static boolean isExceptionThrown(PsiClassType exceptionType, PsiClassType[] thrownTypes) {
for (final PsiClassType type : thrownTypes) {
if (type.equals(exceptionType)) return true;
}
return false;
}
@Override public void visitNewExpression(PsiNewExpression expression) {
processCall(expression);
super.visitNewExpression(expression);
}
@Override
public void visitEnumConstant(PsiEnumConstant enumConstant) {
processCall(enumConstant);
super.visitEnumConstant(enumConstant);
}
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {
//expression.acceptChildren(this);
}
private void processCall(PsiCall expression){
PsiExpressionList argumentList = expression.getArgumentList();
if (argumentList == null) return;
PsiExpression[] args = argumentList.getExpressions();
PsiMethod oldMethod = expression.resolveMethod();
if (oldMethod == null) return;
PsiParameter[] parameters = oldMethod.getParameterList().getParameters();
try {
for (int i = 0; i < args.length; i++) {
final PsiExpression arg = deparenthesizeExpression(args[i]);
if (arg instanceof PsiTypeCastExpression) {
PsiTypeCastExpression cast = (PsiTypeCastExpression)arg;
if (i == args.length - 1 && args.length == parameters.length && parameters[i].isVarArgs()) {
//do not mark cast to resolve ambiguity for calling varargs method with inexact argument
continue;
}
PsiCall newCall = (PsiCall) expression.copy();
final PsiExpressionList argList = newCall.getArgumentList();
LOG.assertTrue(argList != null);
PsiExpression[] newArgs = argList.getExpressions();
PsiTypeCastExpression castExpression = (PsiTypeCastExpression) deparenthesizeExpression(newArgs[i]);
PsiExpression castOperand = castExpression.getOperand();
if (castOperand == null) return;
castExpression.replace(castOperand);
if (newCall instanceof PsiEnumConstant) {
// do this manually, because PsiEnumConstantImpl.resolveMethodGenerics() will assert (no containing class for the copy)
final PsiEnumConstant enumConstant = (PsiEnumConstant)expression;
PsiClass containingClass = enumConstant.getContainingClass();
final JavaPsiFacade facade = JavaPsiFacade.getInstance(enumConstant.getProject());
final PsiClassType type = facade.getElementFactory().createType(containingClass);
final JavaResolveResult newResult = facade.getResolveHelper().resolveConstructor(type, newCall.getArgumentList(), enumConstant);
if (oldMethod.equals(newResult.getElement()) && newResult.isValidResult()) {
addToResults(cast);
}
} else {
final JavaResolveResult newResult = newCall.resolveMethodGenerics();
if (oldMethod.equals(newResult.getElement()) && newResult.isValidResult() &&
Comparing.equal(((PsiCallExpression)newCall).getType(), ((PsiCallExpression)expression).getType())) {
addToResults(cast);
}
}
}
}
}
catch (IncorrectOperationException e) {
return;
}
for (PsiExpression arg : args) {
if (arg instanceof PsiTypeCastExpression) {
PsiExpression castOperand = ((PsiTypeCastExpression)arg).getOperand();
if (castOperand != null) {
castOperand.accept(this);
}
}
else {
arg.accept(this);
}
}
}
@Override public void visitTypeCastExpression(PsiTypeCastExpression typeCast) {
PsiExpression operand = typeCast.getOperand();
if (operand == null) return;
PsiElement expr = deparenthesizeExpression(operand);
final PsiType topCastType = typeCast.getType();
if (expr instanceof PsiTypeCastExpression) {
PsiTypeElement typeElement = ((PsiTypeCastExpression)expr).getCastType();
if (typeElement == null) return;
PsiType castType = typeElement.getType();
final PsiExpression innerOperand = ((PsiTypeCastExpression)expr).getOperand();
final PsiType operandType = innerOperand != null ? innerOperand.getType() : null;
if (!(castType instanceof PsiPrimitiveType) && !(topCastType instanceof PsiPrimitiveType)) {
if (operandType != null && topCastType != null && TypeConversionUtil.areTypesConvertible(operandType, topCastType)) {
addToResults((PsiTypeCastExpression)expr);
}
} else if (PsiPrimitiveType.getUnboxedType(operandType) == topCastType) {
addToResults((PsiTypeCastExpression)expr);
}
}
else {
PsiElement parent = typeCast.getParent();
if (parent instanceof PsiConditionalExpression) {
//branches need to be of the same type
final PsiType operandType = operand.getType();
final PsiType conditionalType = ((PsiConditionalExpression)parent).getType();
if (!Comparing.equal(operandType, conditionalType)) {
if (!PsiUtil.isLanguageLevel5OrHigher(typeCast)) {
return;
}
if (!checkResolveAfterRemoveCast(parent)) return;
final PsiExpression thenExpression = ((PsiConditionalExpression)parent).getThenExpression();
final PsiExpression elseExpression = ((PsiConditionalExpression)parent).getElseExpression();
final PsiExpression opposite = thenExpression == typeCast ? elseExpression : thenExpression;
if (opposite == null || !Comparing.equal(conditionalType, opposite.getType())) return;
}
} else if (parent instanceof PsiSynchronizedStatement && (expr instanceof PsiExpression && ((PsiExpression)expr).getType() instanceof PsiPrimitiveType)) {
return;
} else if (expr instanceof PsiLambdaExpression || expr instanceof PsiMethodReferenceExpression) {
if (parent instanceof PsiParenthesizedExpression && parent.getParent() instanceof PsiReferenceExpression) {
return;
}
final PsiType functionalInterfaceType = LambdaUtil.getFunctionalInterfaceType(typeCast, true);
if (topCastType != null && functionalInterfaceType != null && !TypeConversionUtil.isAssignable(topCastType, functionalInterfaceType, false)) return;
}
processAlreadyHasTypeCast(typeCast);
}
super.visitTypeCastExpression(typeCast);
}
private static boolean checkResolveAfterRemoveCast(PsiElement parent) {
PsiElement grandPa = parent.getParent();
if (grandPa instanceof PsiExpressionList) {
PsiExpression[] expressions = ((PsiExpressionList)grandPa).getExpressions();
int idx = ArrayUtil.find(expressions, parent);
PsiElement grandGrandPa = grandPa.getParent();
if (grandGrandPa instanceof PsiCall) {
PsiElement resolve = ((PsiCall)grandGrandPa).resolveMethod();
if (resolve instanceof PsiMethod) {
PsiCall expression = (PsiCall)grandGrandPa.copy();
PsiExpressionList argumentList = expression.getArgumentList();
LOG.assertTrue(argumentList != null);
PsiExpression toReplace = argumentList.getExpressions()[idx];
if (toReplace instanceof PsiConditionalExpression) {
PsiExpression thenExpression = ((PsiConditionalExpression)toReplace).getThenExpression();
PsiExpression elseExpression = ((PsiConditionalExpression)toReplace).getElseExpression();
if (thenExpression instanceof PsiTypeCastExpression) {
final PsiExpression thenOperand = ((PsiTypeCastExpression)thenExpression).getOperand();
if (thenOperand != null) {
thenExpression.replace(thenOperand);
}
} else if (elseExpression instanceof PsiTypeCastExpression) {
final PsiExpression elseOperand = ((PsiTypeCastExpression)elseExpression).getOperand();
if (elseOperand != null) {
elseExpression.replace(elseOperand);
}
}
}
if (expression.resolveMethod() != resolve) {
return false;
}
}
}
}
return true;
}
private void processAlreadyHasTypeCast(PsiTypeCastExpression typeCast){
PsiElement parent = typeCast.getParent();
while(parent instanceof PsiParenthesizedExpression) parent = parent.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 (isTypeCastSemantic(typeCast)) return;
PsiTypeElement typeElement = typeCast.getCastType();
if (typeElement == null) return;
final PsiType castTo = typeElement.getType();
final PsiExpression operand = typeCast.getOperand();
PsiType opType = operand.getType();
final PsiType expectedTypeByParent = PsiTypesUtil.getExpectedTypeByParent(typeCast);
if (expectedTypeByParent != null) {
try {
final Project project = operand.getProject();
final String uniqueVariableName = JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName("l", parent, false);
final PsiDeclarationStatement declarationStatement =
(PsiDeclarationStatement)JavaPsiFacade.getElementFactory(project).createStatementFromText(
expectedTypeByParent.getCanonicalText() + " " + uniqueVariableName + " = " + operand.getText() + ";", parent);
final PsiExpression initializer = ((PsiLocalVariable)declarationStatement.getDeclaredElements()[0]).getInitializer();
LOG.assertTrue(initializer != null, operand.getText());
opType = initializer.getType();
}
catch (IncorrectOperationException ignore) {}
}
if (opType == null) return;
if (parent instanceof PsiReferenceExpression) {
if (castTo instanceof PsiClassType && opType instanceof PsiPrimitiveType) return; //explicit boxing
//Check accessibility
if (opType instanceof PsiClassType) {
final PsiReferenceExpression refExpression = (PsiReferenceExpression)parent;
PsiElement element = refExpression.resolve();
if (!(element instanceof PsiMember)) return;
PsiClass accessClass = ((PsiClassType)opType).resolve();
if (accessClass == null) return;
if (!JavaPsiFacade.getInstance(parent.getProject()).getResolveHelper().isAccessible((PsiMember)element, typeCast, accessClass)) return;
if (!isCastRedundantInRefExpression(refExpression, operand)) return;
}
}
if (arrayAccessAtTheLeftSideOfAssignment(parent)) {
if (TypeConversionUtil.isAssignable(opType, castTo, false) && opType.getArrayDimensions() == castTo.getArrayDimensions()) {
addToResults(typeCast);
}
}
else {
if (parent instanceof PsiInstanceOfExpression && opType instanceof PsiPrimitiveType) {
return;
}
if (parent instanceof PsiForeachStatement) {
final PsiClassType.ClassResolveResult castResolveResult = PsiUtil.resolveGenericsClassInType(opType);
final PsiClass psiClass = castResolveResult.getElement();
if (psiClass != null) {
final PsiClass iterableClass = JavaPsiFacade.getInstance(parent.getProject()).findClass(CommonClassNames.JAVA_LANG_ITERABLE, psiClass.getResolveScope());
if (iterableClass != null && InheritanceUtil.isInheritorOrSelf(psiClass, iterableClass, true)) {
final PsiTypeParameter[] iterableTypeParameters = iterableClass.getTypeParameters();
if (iterableTypeParameters.length == 1) {
final PsiType resultedParamType = TypeConversionUtil.getSuperClassSubstitutor(iterableClass, psiClass, castResolveResult.getSubstitutor()).substitute(iterableTypeParameters[0]);
if (resultedParamType != null &&
TypeConversionUtil.isAssignable(((PsiForeachStatement)parent).getIterationParameter().getType(), resultedParamType)) {
addToResults(typeCast);
return;
}
}
}
} else {
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);
if (method != null) {
for (PsiClassType thrownType : method.getThrowsList().getReferencedTypes()) {
if (TypeConversionUtil.isAssignable(thrownType, opType, false)) {
addToResults(typeCast);
return;
}
}
}
}
}
if (parent instanceof PsiInstanceOfExpression) {
//15.20.2. Type Comparison Operator instanceof:
//If a cast (§15.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);
}
}
else if (TypeConversionUtil.isAssignable(castTo, opType, false) &&
(expectedTypeByParent == null || TypeConversionUtil.isAssignable(expectedTypeByParent, opType, false))) {
addToResults(typeCast);
}
}
}
private static boolean arrayAccessAtTheLeftSideOfAssignment(PsiElement element) {
PsiAssignmentExpression assignment = PsiTreeUtil.getParentOfType(element, PsiAssignmentExpression.class, false, PsiMember.class);
if (assignment == null) return false;
PsiExpression lExpression = assignment.getLExpression();
return PsiTreeUtil.isAncestor(lExpression, element, false) && lExpression instanceof PsiArrayAccessExpression;
}
}
private static boolean isCastRedundantInRefExpression (final PsiReferenceExpression refExpression, final PsiExpression castOperand) {
final PsiElement resolved = refExpression.resolve();
final Ref<Boolean> result = new Ref<Boolean>(Boolean.FALSE);
CodeStyleManager.getInstance(refExpression.getProject()).performActionWithFormatterDisabled(new Runnable() {
@Override
public void run() {
try {
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(refExpression.getProject()).getElementFactory();
final PsiExpression copyExpression = elementFactory.createExpressionFromText(refExpression.getText(), refExpression);
if (copyExpression instanceof PsiReferenceExpression) {
final PsiReferenceExpression copy = (PsiReferenceExpression)copyExpression;
final PsiExpression qualifier = copy.getQualifierExpression();
if (qualifier != null) {
qualifier.replace(castOperand);
result.set(Boolean.valueOf(copy.resolve() == resolved));
}
}
}
catch (IncorrectOperationException ignore) { }
}
});
return result.get().booleanValue();
}
/** @deprecated use {@link #isTypeCastSemantic(PsiTypeCastExpression)} (to remove in IDEA 14) */
@SuppressWarnings({"UnusedDeclaration", "SpellCheckingInspection"})
public static boolean isTypeCastSemantical(PsiTypeCastExpression typeCast) {
return isTypeCastSemantic(typeCast);
}
public static boolean isTypeCastSemantic(PsiTypeCastExpression typeCast) {
PsiExpression operand = typeCast.getOperand();
if (operand == null) return false;
if (isInPolymorphicCall(typeCast)) return true;
PsiType opType = operand.getType();
PsiTypeElement typeElement = typeCast.getCastType();
if (typeElement == null) return false;
PsiType castType = typeElement.getType();
if (castType instanceof PsiPrimitiveType) {
if (opType instanceof PsiPrimitiveType) {
return !opType.equals(castType); // let's suppose all not equal primitive casts are necessary
}
final PsiPrimitiveType unboxedOpType = PsiPrimitiveType.getUnboxedType(opType);
if (unboxedOpType != null && !unboxedOpType.equals(castType) ) {
return true;
}
}
else if (castType instanceof PsiClassType && ((PsiClassType)castType).hasParameters()) {
if (opType instanceof PsiClassType && ((PsiClassType)opType).isRaw()) return true;
} else if (castType instanceof PsiClassType && ((PsiClassType)castType).isRaw()) {
if (opType instanceof PsiClassType && ((PsiClassType)opType).hasParameters()) return true;
}
if (operand instanceof PsiLambdaExpression || operand instanceof PsiMethodReferenceExpression) {
if (castType instanceof PsiClassType && InheritanceUtil.isInheritor(PsiUtil.resolveClassInType(castType), CommonClassNames.JAVA_IO_SERIALIZABLE)) return true;
if (castType instanceof PsiIntersectionType) {
boolean redundant = false;
final PsiType[] conjuncts = ((PsiIntersectionType)castType).getConjuncts();
for (int i = 1; i < conjuncts.length; i++) {
PsiType conjunct = conjuncts[i];
if (TypeConversionUtil.isAssignable(conjuncts[0], conjunct)) {
redundant = true;
break;
}
}
if (!redundant) {
return true;
}
}
}
PsiElement parent = typeCast.getParent();
while(parent instanceof PsiParenthesizedExpression) parent = parent.getParent();
if (parent instanceof PsiBinaryExpression) {
PsiBinaryExpression expression = (PsiBinaryExpression)parent;
PsiExpression firstOperand = expression.getLOperand();
PsiExpression otherOperand = expression.getROperand();
if (PsiTreeUtil.isAncestor(otherOperand, typeCast, false)) {
PsiExpression temp = otherOperand;
otherOperand = firstOperand;
firstOperand = temp;
}
if (firstOperand != null && otherOperand != null && wrapperCastChangeSemantics(firstOperand, otherOperand, operand)) {
return true;
}
} else if (parent instanceof PsiConditionalExpression) {
if (opType instanceof PsiPrimitiveType && !(((PsiConditionalExpression)parent).getType() instanceof PsiPrimitiveType)) {
if (PsiPrimitiveType.getUnboxedType(PsiTypesUtil.getExpectedTypeByParent((PsiExpression)parent)) != null) {
return true;
}
}
}
return false;
}
private static boolean wrapperCastChangeSemantics(PsiExpression operand, PsiExpression otherOperand, PsiExpression toCast) {
final boolean isPrimitiveComparisonWithCast;
final boolean isPrimitiveComparisonWithoutCast;
if (TypeConversionUtil.isPrimitiveAndNotNull(otherOperand.getType())) {
// IDEA-111450: A primitive comparison requires one primitive operand and one primitive or wrapper operand.
isPrimitiveComparisonWithCast = TypeConversionUtil.isPrimitiveAndNotNullOrWrapper(operand.getType());
isPrimitiveComparisonWithoutCast = TypeConversionUtil.isPrimitiveAndNotNullOrWrapper(toCast.getType());
}
else {
// We do not check whether `otherOperand` is a wrapper, because a reference-to-primitive cast has a
// side effect regardless of whether we end up doing a primitive or reference comparison.
isPrimitiveComparisonWithCast = TypeConversionUtil.isPrimitiveAndNotNull(operand.getType());
isPrimitiveComparisonWithoutCast = TypeConversionUtil.isPrimitiveAndNotNull(toCast.getType());
}
// wrapper casted to primitive vs wrapper comparison
return isPrimitiveComparisonWithCast != isPrimitiveComparisonWithoutCast;
}
// see http://download.java.net/jdk7/docs/api/java/lang/invoke/MethodHandle.html#sigpoly
public static boolean isInPolymorphicCall(final PsiTypeCastExpression typeCast) {
if (!PsiUtil.isLanguageLevel7OrHigher(typeCast)) return false;
// return type
final PsiExpression operand = typeCast.getOperand();
if (operand instanceof PsiMethodCallExpression) {
if (isPolymorphicMethod((PsiMethodCallExpression)operand)) return true;
}
// argument type
final PsiElement exprList = typeCast.getParent();
if (exprList instanceof PsiExpressionList) {
final PsiElement methodCall = exprList.getParent();
if (methodCall instanceof PsiMethodCallExpression) {
if (isPolymorphicMethod((PsiMethodCallExpression)methodCall)) return true;
}
}
return false;
}
private static boolean isPolymorphicMethod(PsiMethodCallExpression expression) {
final PsiElement method = expression.getMethodExpression().resolve();
return method instanceof PsiMethod &&
AnnotationUtil.isAnnotated((PsiMethod)method, CommonClassNames.JAVA_LANG_INVOKE_MH_POLYMORPHIC, false, true);
}
}