2 * Copyright 2000-2011 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.psi.util;
18 import com.intellij.codeInsight.AnnotationUtil;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.util.Comparing;
21 import com.intellij.openapi.util.Ref;
22 import com.intellij.psi.*;
23 import com.intellij.psi.codeStyle.CodeStyleManager;
24 import com.intellij.psi.tree.IElementType;
25 import com.intellij.util.ArrayUtil;
26 import com.intellij.util.IncorrectOperationException;
27 import org.jetbrains.annotations.NotNull;
28 import org.jetbrains.annotations.Nullable;
36 public class RedundantCastUtil {
37 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.redundantCast.RedundantCastUtil");
39 private RedundantCastUtil() { }
42 public static List<PsiTypeCastExpression> getRedundantCastsInside(PsiElement where) {
43 MyCollectingVisitor visitor = new MyCollectingVisitor();
44 if (where instanceof PsiEnumConstant) {
45 where.accept(visitor);
47 where.acceptChildren(visitor);
49 return new ArrayList<PsiTypeCastExpression>(visitor.myFoundCasts);
52 public static boolean isCastRedundant (PsiTypeCastExpression typeCast) {
53 PsiElement parent = typeCast.getParent();
54 while(parent instanceof PsiParenthesizedExpression) parent = parent.getParent();
55 if (parent instanceof PsiExpressionList) parent = parent.getParent();
56 if (parent instanceof PsiReferenceExpression) parent = parent.getParent();
57 MyIsRedundantVisitor visitor = new MyIsRedundantVisitor(false);
58 parent.accept(visitor);
59 return visitor.isRedundant;
63 private static PsiExpression deparenthesizeExpression(PsiExpression arg) {
64 while (arg instanceof PsiParenthesizedExpression) arg = ((PsiParenthesizedExpression) arg).getExpression();
68 private static class MyCollectingVisitor extends MyIsRedundantVisitor {
69 private final Set<PsiTypeCastExpression> myFoundCasts = new HashSet<PsiTypeCastExpression>();
71 private MyCollectingVisitor() {
75 @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
76 expression.acceptChildren(this);
79 @Override public void visitClass(PsiClass aClass) {
80 // avoid multiple visit
83 @Override public void visitMethod(PsiMethod method) {
84 // avoid multiple visit
87 @Override public void visitField(PsiField field) {
88 // avoid multiple visit
92 protected void addToResults(@NotNull PsiTypeCastExpression typeCast){
93 if (!isTypeCastSemantical(typeCast)) {
94 myFoundCasts.add(typeCast);
99 private static class MyIsRedundantVisitor extends JavaRecursiveElementVisitor {
100 private boolean isRedundant = false;
101 private final boolean myRecursive;
103 private MyIsRedundantVisitor(final boolean recursive) {
104 myRecursive = recursive;
108 public void visitElement(final PsiElement element) {
110 super.visitElement(element);
114 protected void addToResults(@NotNull PsiTypeCastExpression typeCast){
115 if (!isTypeCastSemantical(typeCast)) {
120 @Override public void visitAssignmentExpression(PsiAssignmentExpression expression) {
121 processPossibleTypeCast(expression.getRExpression(), expression.getLExpression().getType());
122 super.visitAssignmentExpression(expression);
125 @Override public void visitVariable(PsiVariable variable) {
126 processPossibleTypeCast(variable.getInitializer(), variable.getType());
127 super.visitVariable(variable);
130 @Override public void visitReturnStatement(PsiReturnStatement statement) {
131 final PsiMethod method = PsiTreeUtil.getParentOfType(statement, PsiMethod.class);
132 if (method != null) {
133 final PsiType returnType = method.getReturnType();
134 final PsiExpression returnValue = statement.getReturnValue();
135 if (returnValue != null) {
136 processPossibleTypeCast(returnValue, returnType);
139 super.visitReturnStatement(statement);
143 public void visitPolyadicExpression(PsiPolyadicExpression expression) {
144 IElementType tokenType = expression.getOperationTokenType();
145 PsiExpression[] operands = expression.getOperands();
146 if (operands.length >= 2) {
147 PsiType lType = operands[0].getType();
148 processBinaryExpressionOperand(deparenthesizeExpression(operands[0]), operands[1].getType(), tokenType);
149 for (int i = 1; i < operands.length; i++) {
150 PsiExpression operand = deparenthesizeExpression(operands[i]);
151 if (operand == null) continue;
152 processBinaryExpressionOperand(operand, lType, tokenType);
153 lType = TypeConversionUtil.calcTypeForBinaryExpression(lType, operand.getType(), tokenType, true);
156 super.visitPolyadicExpression(expression);
159 private void processBinaryExpressionOperand(final PsiExpression operand,
160 final PsiType otherType,
161 final IElementType binaryToken) {
162 if (operand instanceof PsiTypeCastExpression) {
163 PsiTypeCastExpression typeCast = (PsiTypeCastExpression)operand;
164 PsiExpression toCast = typeCast.getOperand();
165 if (toCast != null && TypeConversionUtil.isBinaryOperatorApplicable(binaryToken, toCast.getType(), otherType, false)) {
166 addToResults(typeCast);
171 private void processPossibleTypeCast(PsiExpression rExpr, @Nullable PsiType lType) {
172 rExpr = deparenthesizeExpression(rExpr);
173 if (rExpr instanceof PsiTypeCastExpression) {
174 PsiExpression castOperand = ((PsiTypeCastExpression)rExpr).getOperand();
175 if (castOperand != null) {
176 PsiType operandType = castOperand.getType();
177 if (operandType != null) {
178 if (lType != null && TypeConversionUtil.isAssignable(lType, operandType, false)) {
179 addToResults((PsiTypeCastExpression)rExpr);
186 @Override public void visitMethodCallExpression(PsiMethodCallExpression expression) {
187 processCall(expression);
189 checkForVirtual(expression);
190 super.visitMethodCallExpression(expression);
193 private void checkForVirtual(PsiMethodCallExpression methodCall) {
194 PsiReferenceExpression methodExpr = methodCall.getMethodExpression();
195 PsiExpression qualifier = methodExpr.getQualifierExpression();
196 if (!(qualifier instanceof PsiParenthesizedExpression)) return;
197 PsiExpression operand = ((PsiParenthesizedExpression)qualifier).getExpression();
198 if (!(operand instanceof PsiTypeCastExpression)) return;
199 PsiTypeCastExpression typeCast = (PsiTypeCastExpression)operand;
200 PsiExpression castOperand = typeCast.getOperand();
201 if (castOperand == null) return;
203 PsiType type = castOperand.getType();
204 if (type == null) return;
205 if (type instanceof PsiPrimitiveType) return;
207 final JavaResolveResult resolveResult = methodExpr.advancedResolve(false);
208 PsiMethod targetMethod = (PsiMethod)resolveResult.getElement();
209 if (targetMethod == null) return;
210 if (targetMethod.hasModifierProperty(PsiModifier.STATIC)) return;
213 PsiManager manager = methodExpr.getManager();
214 PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
216 PsiMethodCallExpression newCall = (PsiMethodCallExpression)factory.createExpressionFromText(methodCall.getText(), methodCall);
217 PsiExpression newQualifier = newCall.getMethodExpression().getQualifierExpression();
218 PsiExpression newOperand = ((PsiTypeCastExpression)((PsiParenthesizedExpression)newQualifier).getExpression()).getOperand();
219 newQualifier.replace(newOperand);
221 final JavaResolveResult newResult = newCall.getMethodExpression().advancedResolve(false);
222 if (!newResult.isValidResult()) return;
223 final PsiMethod newTargetMethod = (PsiMethod)newResult.getElement();
224 final PsiType newReturnType = newResult.getSubstitutor().substitute(newTargetMethod.getReturnType());
225 final PsiType oldReturnType = resolveResult.getSubstitutor().substitute(targetMethod.getReturnType());
226 if (Comparing.equal(newReturnType, oldReturnType)) {
227 if (newTargetMethod.equals(targetMethod) ||
228 (newTargetMethod.getSignature(newResult.getSubstitutor()).equals(targetMethod.getSignature(resolveResult.getSubstitutor())) &&
229 !(newTargetMethod.isDeprecated() && !targetMethod.isDeprecated()) && // see SCR11555, SCR14559
230 areThrownExceptionsCompatible(targetMethod, newTargetMethod))) {
231 addToResults(typeCast);
235 catch (IncorrectOperationException ignore) { }
238 private static boolean areThrownExceptionsCompatible(final PsiMethod targetMethod, final PsiMethod newTargetMethod) {
239 final PsiClassType[] oldThrowsTypes = targetMethod.getThrowsList().getReferencedTypes();
240 final PsiClassType[] newThrowsTypes = newTargetMethod.getThrowsList().getReferencedTypes();
241 for (final PsiClassType throwsType : newThrowsTypes) {
242 if (!isExceptionThrown(throwsType, oldThrowsTypes)) return false;
247 private static boolean isExceptionThrown(PsiClassType exceptionType, PsiClassType[] thrownTypes) {
248 for (final PsiClassType type : thrownTypes) {
249 if (type.equals(exceptionType)) return true;
254 @Override public void visitNewExpression(PsiNewExpression expression) {
255 processCall(expression);
256 super.visitNewExpression(expression);
260 public void visitEnumConstant(PsiEnumConstant enumConstant) {
261 processCall(enumConstant);
262 super.visitEnumConstant(enumConstant);
265 @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
266 //expression.acceptChildren(this);
269 private void processCall(PsiCall expression){
270 PsiExpressionList argumentList = expression.getArgumentList();
271 if (argumentList == null) return;
272 PsiExpression[] args = argumentList.getExpressions();
273 PsiMethod oldMethod = expression.resolveMethod();
274 if (oldMethod == null) return;
275 PsiParameter[] parameters = oldMethod.getParameterList().getParameters();
278 for (int i = 0; i < args.length; i++) {
279 final PsiExpression arg = deparenthesizeExpression(args[i]);
280 if (arg instanceof PsiTypeCastExpression) {
281 PsiTypeCastExpression cast = (PsiTypeCastExpression)arg;
282 if (i == args.length - 1 && args.length == parameters.length && parameters[i].isVarArgs()) {
283 //do not mark cast to resolve ambiguity for calling varargs method with inexact argument
286 PsiCall newCall = (PsiCall) expression.copy();
287 final PsiExpressionList argList = newCall.getArgumentList();
288 LOG.assertTrue(argList != null);
289 PsiExpression[] newArgs = argList.getExpressions();
290 PsiTypeCastExpression castExpression = (PsiTypeCastExpression) deparenthesizeExpression(newArgs[i]);
291 PsiExpression castOperand = castExpression.getOperand();
292 if (castOperand == null) return;
293 castExpression.replace(castOperand);
294 if (newCall instanceof PsiEnumConstant) {
295 // do this manually, because PsiEnumConstantImpl.resolveMethodGenerics() will assert (no containing class for the copy)
296 final PsiEnumConstant enumConstant = (PsiEnumConstant)expression;
297 PsiClass containingClass = enumConstant.getContainingClass();
298 final JavaPsiFacade facade = JavaPsiFacade.getInstance(enumConstant.getProject());
299 final PsiClassType type = facade.getElementFactory().createType(containingClass);
300 final JavaResolveResult newResult = facade.getResolveHelper().resolveConstructor(type, newCall.getArgumentList(), enumConstant);
301 if (oldMethod.equals(newResult.getElement()) && newResult.isValidResult()) {
305 final JavaResolveResult newResult = newCall.resolveMethodGenerics();
306 if (oldMethod.equals(newResult.getElement()) && newResult.isValidResult() &&
307 Comparing.equal(((PsiCallExpression)newCall).getType(), ((PsiCallExpression)expression).getType())) {
314 catch (IncorrectOperationException e) {
318 for (PsiExpression arg : args) {
319 if (arg instanceof PsiTypeCastExpression) {
320 PsiExpression castOperand = ((PsiTypeCastExpression)arg).getOperand();
321 if (castOperand != null) {
322 castOperand.accept(this);
331 @Override public void visitTypeCastExpression(PsiTypeCastExpression typeCast) {
332 PsiExpression operand = typeCast.getOperand();
333 if (operand == null) return;
335 PsiElement expr = deparenthesizeExpression(operand);
337 if (expr instanceof PsiTypeCastExpression) {
338 PsiTypeElement typeElement = ((PsiTypeCastExpression)expr).getCastType();
339 if (typeElement == null) return;
340 PsiType castType = typeElement.getType();
341 final PsiExpression innerOperand = ((PsiTypeCastExpression)expr).getOperand();
342 final PsiType operandType = innerOperand != null ? innerOperand.getType() : null;
343 final PsiType topCastType = typeCast.getType();
344 if (!(castType instanceof PsiPrimitiveType)) {
345 if (operandType != null && topCastType != null && TypeConversionUtil.areTypesConvertible(operandType, topCastType)) {
346 addToResults((PsiTypeCastExpression)expr);
348 } else if (PsiPrimitiveType.getUnboxedType(operandType) == topCastType) {
349 addToResults((PsiTypeCastExpression)expr);
353 PsiElement parent = typeCast.getParent();
354 if (parent instanceof PsiConditionalExpression) {
355 //branches need to be of the same type
356 if (!Comparing.equal(operand.getType(), ((PsiConditionalExpression)parent).getType())) {
357 if (!PsiUtil.isLanguageLevel5OrHigher(typeCast)) {
360 if (!checkResolveAfterRemoveCast(parent)) return;
362 } else if (parent instanceof PsiSynchronizedStatement && (expr instanceof PsiExpression && ((PsiExpression)expr).getType() instanceof PsiPrimitiveType)) {
365 processAlreadyHasTypeCast(typeCast);
367 super.visitTypeCastExpression(typeCast);
370 private static boolean checkResolveAfterRemoveCast(PsiElement parent) {
371 PsiElement grandPa = parent.getParent();
372 if (grandPa instanceof PsiExpressionList) {
373 PsiExpression[] expressions = ((PsiExpressionList)grandPa).getExpressions();
374 int idx = ArrayUtil.find(expressions, parent);
375 PsiElement grandGrandPa = grandPa.getParent();
376 if (grandGrandPa instanceof PsiCall) {
377 PsiElement resolve = ((PsiCall)grandGrandPa).resolveMethod();
378 if (resolve instanceof PsiMethod) {
379 PsiCall expression = (PsiCall)grandGrandPa.copy();
380 PsiExpressionList argumentList = expression.getArgumentList();
381 LOG.assertTrue(argumentList != null);
382 PsiExpression toReplace = argumentList.getExpressions()[idx];
383 if (toReplace instanceof PsiConditionalExpression) {
384 PsiExpression thenExpression = ((PsiConditionalExpression)toReplace).getThenExpression();
385 PsiExpression elseExpression = ((PsiConditionalExpression)toReplace).getElseExpression();
386 if (thenExpression instanceof PsiTypeCastExpression) {
387 final PsiExpression thenOperand = ((PsiTypeCastExpression)thenExpression).getOperand();
388 if (thenOperand != null) {
389 thenExpression.replace(thenOperand);
391 } else if (elseExpression instanceof PsiTypeCastExpression) {
392 final PsiExpression elseOperand = ((PsiTypeCastExpression)elseExpression).getOperand();
393 if (elseOperand != null) {
394 elseExpression.replace(elseOperand);
398 if (expression.resolveMethod() != resolve) {
407 private void processAlreadyHasTypeCast(PsiTypeCastExpression typeCast){
408 PsiElement parent = typeCast.getParent();
409 while(parent instanceof PsiParenthesizedExpression) parent = parent.getParent();
410 if (parent instanceof PsiExpressionList) return; // do not replace in arg lists - should be handled by parent
411 if (parent instanceof PsiReturnStatement) return;
412 if (parent instanceof PsiTypeCastExpression) return;
414 if (isTypeCastSemantical(typeCast)) return;
416 PsiTypeElement typeElement = typeCast.getCastType();
417 if (typeElement == null) return;
418 PsiType castTo = typeElement.getType();
419 PsiType opType = typeCast.getOperand().getType();
420 if (opType == null) return;
421 if (parent instanceof PsiReferenceExpression) {
422 if (castTo instanceof PsiClassType && opType instanceof PsiPrimitiveType) return; //explicit boxing
423 //Check accessibility
424 if (opType instanceof PsiClassType) {
425 final PsiReferenceExpression refExpression = (PsiReferenceExpression)parent;
426 PsiElement element = refExpression.resolve();
427 if (!(element instanceof PsiMember)) return;
428 PsiClass accessClass = ((PsiClassType)opType).resolve();
429 if (accessClass == null) return;
430 if (!JavaPsiFacade.getInstance(parent.getProject()).getResolveHelper().isAccessible((PsiMember)element, typeCast, accessClass)) return;
431 if (!isCastRedundantInRefExpression(refExpression, typeCast.getOperand())) return;
435 if (someWhereAtTheLeftSideOfAssignment(parent)) {
436 if (TypeConversionUtil.isAssignable(opType, castTo, false)) {
437 addToResults(typeCast);
441 if (TypeConversionUtil.isAssignable(castTo, opType, false)) {
442 addToResults(typeCast);
447 private static boolean someWhereAtTheLeftSideOfAssignment(PsiElement element) {
448 PsiAssignmentExpression assignment = PsiTreeUtil.getParentOfType(element, PsiAssignmentExpression.class, false, PsiMember.class);
449 if (assignment == null) return false;
450 PsiExpression lExpression = assignment.getLExpression();
451 return PsiTreeUtil.isAncestor(lExpression, element, false);
455 private static boolean isCastRedundantInRefExpression (final PsiReferenceExpression refExpression, final PsiExpression castOperand) {
456 final PsiElement resolved = refExpression.resolve();
457 final Ref<Boolean> result = new Ref<Boolean>(Boolean.FALSE);
458 CodeStyleManager.getInstance(refExpression.getProject()).performActionWithFormatterDisabled(new Runnable() {
462 final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(refExpression.getProject()).getElementFactory();
463 final PsiExpression copyExpression = elementFactory.createExpressionFromText(refExpression.getText(), refExpression);
464 if (copyExpression instanceof PsiReferenceExpression) {
465 final PsiReferenceExpression copy = (PsiReferenceExpression)copyExpression;
466 final PsiExpression qualifier = copy.getQualifierExpression();
467 if (qualifier != null) {
468 qualifier.replace(castOperand);
469 result.set(Boolean.valueOf(copy.resolve() == resolved));
473 catch (IncorrectOperationException ignore) { }
476 return result.get().booleanValue();
479 public static boolean isTypeCastSemantical(PsiTypeCastExpression typeCast) {
480 PsiExpression operand = typeCast.getOperand();
481 if (operand == null) return false;
483 if (isInPolymorphicCall(typeCast)) return true;
485 PsiType opType = operand.getType();
486 PsiTypeElement typeElement = typeCast.getCastType();
487 if (typeElement == null) return false;
489 PsiType castType = typeElement.getType();
490 if (castType instanceof PsiPrimitiveType) {
491 if (opType instanceof PsiPrimitiveType) {
492 return !opType.equals(castType); // let's suppose all not equal primitive casts are necessary
494 final PsiPrimitiveType unboxedOpType = PsiPrimitiveType.getUnboxedType(opType);
495 if (unboxedOpType != null && !unboxedOpType.equals(castType) ) {
499 else if (castType instanceof PsiClassType && ((PsiClassType)castType).hasParameters()) {
500 if (opType instanceof PsiClassType && ((PsiClassType)opType).isRaw()) return true;
501 } else if (castType instanceof PsiClassType && ((PsiClassType)castType).isRaw()) {
502 if (opType instanceof PsiClassType && ((PsiClassType)opType).hasParameters()) return true;
505 PsiElement parent = typeCast.getParent();
506 while(parent instanceof PsiParenthesizedExpression) parent = parent.getParent();
508 if (parent instanceof PsiBinaryExpression) {
509 PsiBinaryExpression expression = (PsiBinaryExpression)parent;
510 PsiExpression firstOperand = expression.getLOperand();
511 PsiExpression otherOperand = expression.getROperand();
512 if (PsiTreeUtil.isAncestor(otherOperand, typeCast, false)) {
513 PsiExpression temp = otherOperand;
514 otherOperand = firstOperand;
517 if (firstOperand != null && otherOperand != null && wrapperCastChangeSemantics(firstOperand, otherOperand, operand)) {
524 private static boolean wrapperCastChangeSemantics(PsiExpression operand, PsiExpression otherOperand, PsiExpression toCast) {
525 boolean isPrimitiveComparisonWithCast = TypeConversionUtil.isPrimitiveAndNotNull(operand.getType()) ||
526 TypeConversionUtil.isPrimitiveAndNotNull(otherOperand.getType());
527 boolean isPrimitiveComparisonWithoutCast = TypeConversionUtil.isPrimitiveAndNotNull(toCast.getType()) ||
528 TypeConversionUtil.isPrimitiveAndNotNull(otherOperand.getType());
529 // wrapper casted to primitive vs wrapper comparison
530 return isPrimitiveComparisonWithCast != isPrimitiveComparisonWithoutCast;
533 // see http://download.java.net/jdk7/docs/api/java/lang/invoke/MethodHandle.html#sigpoly
534 public static boolean isInPolymorphicCall(final PsiTypeCastExpression typeCast) {
535 if (!PsiUtil.isLanguageLevel7OrHigher(typeCast)) return false;
538 final PsiExpression operand = typeCast.getOperand();
539 if (operand instanceof PsiMethodCallExpression) {
540 if (isPolymorphicMethod((PsiMethodCallExpression)operand)) return true;
544 final PsiElement exprList = typeCast.getParent();
545 if (exprList instanceof PsiExpressionList) {
546 final PsiElement methodCall = exprList.getParent();
547 if (methodCall instanceof PsiMethodCallExpression) {
548 if (isPolymorphicMethod((PsiMethodCallExpression)methodCall)) return true;
555 private static boolean isPolymorphicMethod(PsiMethodCallExpression expression) {
556 final PsiElement method = expression.getMethodExpression().resolve();
557 return method instanceof PsiMethod &&
558 AnnotationUtil.isAnnotated((PsiMethod)method, CommonClassNames.JAVA_LANG_INVOKE_MH_POLYMORPHIC, false, true);