remove redundant casts when extract changed parameter type accordingly (IDEA-79743)
[idea/community.git] / java / openapi / src / com / intellij / psi / util / RedundantCastUtil.java
1 /*
2  * Copyright 2000-2011 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.psi.util;
17
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;
29
30 import java.util.ArrayList;
31 import java.util.HashSet;
32 import java.util.List;
33 import java.util.Set;
34
35 /**
36  * @author max
37  * Date: Mar 24, 2002
38  */
39 public class RedundantCastUtil {
40   private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.redundantCast.RedundantCastUtil");
41
42   private RedundantCastUtil() { }
43
44   @NotNull
45   public static List<PsiTypeCastExpression> getRedundantCastsInside(PsiElement where) {
46     MyCollectingVisitor visitor = new MyCollectingVisitor();
47     if (where instanceof PsiEnumConstant) {
48       where.accept(visitor);
49     } else {
50       where.acceptChildren(visitor);
51     }
52     return new ArrayList<PsiTypeCastExpression>(visitor.myFoundCasts);
53   }
54
55   public static boolean isCastRedundant (PsiTypeCastExpression typeCast) {
56     PsiElement parent = typeCast.getParent();
57     while(parent instanceof PsiParenthesizedExpression) parent = parent.getParent();
58     if (parent instanceof PsiExpressionList) parent = parent.getParent();
59     if (parent instanceof PsiReferenceExpression) parent = parent.getParent();
60     MyIsRedundantVisitor visitor = new MyIsRedundantVisitor(false);
61     parent.accept(visitor);
62     return visitor.isRedundant;
63   }
64
65   @Nullable
66   private static PsiExpression deparenthesizeExpression(PsiExpression arg) {
67     while (arg instanceof PsiParenthesizedExpression) arg = ((PsiParenthesizedExpression) arg).getExpression();
68     return arg;
69   }
70
71   public static void removeCast(PsiTypeCastExpression castExpression) {
72     if (castExpression == null) return;
73     PsiExpression operand = castExpression.getOperand();
74     if (operand instanceof PsiParenthesizedExpression) {
75       final PsiParenthesizedExpression parExpr = (PsiParenthesizedExpression)operand;
76       operand = parExpr.getExpression();
77     }
78     if (operand == null) return;
79
80     PsiElement toBeReplaced = castExpression;
81
82     PsiElement parent = castExpression.getParent();
83     while (parent instanceof PsiParenthesizedExpression) {
84       toBeReplaced = parent;
85       parent = parent.getParent();
86     }
87
88     try {
89       toBeReplaced.replace(operand);
90     }
91     catch (IncorrectOperationException e) {
92       LOG.error(e);
93     }
94   }
95
96   private static class MyCollectingVisitor extends MyIsRedundantVisitor {
97     private final Set<PsiTypeCastExpression> myFoundCasts = new HashSet<PsiTypeCastExpression>();
98
99     private MyCollectingVisitor() {
100       super(true);
101     }
102
103     @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
104       expression.acceptChildren(this);
105     }
106
107     @Override public void visitClass(PsiClass aClass) {
108       // avoid multiple visit
109     }
110
111     @Override public void visitMethod(PsiMethod method) {
112       // avoid multiple visit
113     }
114
115     @Override public void visitField(PsiField field) {
116       // avoid multiple visit
117     }
118
119     @Override
120     protected void addToResults(@NotNull PsiTypeCastExpression typeCast){
121       if (!isTypeCastSemantical(typeCast)) {
122         myFoundCasts.add(typeCast);
123       }
124     }
125   }
126
127   private static class MyIsRedundantVisitor extends JavaRecursiveElementVisitor {
128     private boolean isRedundant = false;
129     private final boolean myRecursive;
130
131     private MyIsRedundantVisitor(final boolean recursive) {
132       myRecursive = recursive;
133     }
134
135     @Override
136     public void visitElement(final PsiElement element) {
137       if (myRecursive) {
138         super.visitElement(element);
139       }
140     }
141
142     protected void addToResults(@NotNull PsiTypeCastExpression typeCast){
143       if (!isTypeCastSemantical(typeCast)) {
144         isRedundant = true;
145       }
146     }
147
148     @Override public void visitAssignmentExpression(PsiAssignmentExpression expression) {
149       processPossibleTypeCast(expression.getRExpression(), expression.getLExpression().getType());
150       super.visitAssignmentExpression(expression);
151     }
152
153     @Override public void visitVariable(PsiVariable variable) {
154       processPossibleTypeCast(variable.getInitializer(), variable.getType());
155       super.visitVariable(variable);
156     }
157
158     @Override public void visitReturnStatement(PsiReturnStatement statement) {
159       final PsiMethod method = PsiTreeUtil.getParentOfType(statement, PsiMethod.class);
160       if (method != null) {
161         final PsiType returnType = method.getReturnType();
162         final PsiExpression returnValue = statement.getReturnValue();
163         if (returnValue != null) {
164           processPossibleTypeCast(returnValue, returnType);
165         }
166       }
167       super.visitReturnStatement(statement);
168     }
169
170     @Override
171     public void visitPolyadicExpression(PsiPolyadicExpression expression) {
172       IElementType tokenType = expression.getOperationTokenType();
173       PsiExpression[] operands = expression.getOperands();
174       if (operands.length >= 2) {
175         PsiType lType = operands[0].getType();
176         processBinaryExpressionOperand(deparenthesizeExpression(operands[0]), operands[1].getType(), tokenType);
177         for (int i = 1; i < operands.length; i++) {
178           PsiExpression operand = deparenthesizeExpression(operands[i]);
179           if (operand == null) continue;
180           processBinaryExpressionOperand(operand, lType, tokenType);
181           lType = TypeConversionUtil.calcTypeForBinaryExpression(lType, operand.getType(), tokenType, true);
182         }
183       }
184       super.visitPolyadicExpression(expression);
185     }
186
187     private void processBinaryExpressionOperand(final PsiExpression operand,
188                                                 final PsiType otherType,
189                                                 final IElementType binaryToken) {
190       if (operand instanceof PsiTypeCastExpression) {
191         PsiTypeCastExpression typeCast = (PsiTypeCastExpression)operand;
192         PsiExpression toCast = typeCast.getOperand();
193         if (toCast != null && TypeConversionUtil.isBinaryOperatorApplicable(binaryToken, toCast.getType(), otherType, false)) {
194           addToResults(typeCast);
195         }
196       }
197     }
198
199     private void processPossibleTypeCast(PsiExpression rExpr, @Nullable PsiType lType) {
200       rExpr = deparenthesizeExpression(rExpr);
201       if (rExpr instanceof PsiTypeCastExpression) {
202         PsiExpression castOperand = ((PsiTypeCastExpression)rExpr).getOperand();
203         if (castOperand != null) {
204           PsiType operandType = castOperand.getType();
205           if (operandType != null) {
206             if (lType != null && TypeConversionUtil.isAssignable(lType, operandType, false)) {
207               addToResults((PsiTypeCastExpression)rExpr);
208             }
209           }
210         }
211       }
212     }
213
214     @Override public void visitMethodCallExpression(PsiMethodCallExpression expression) {
215       processCall(expression);
216
217       checkForVirtual(expression);
218       super.visitMethodCallExpression(expression);
219     }
220
221     private void checkForVirtual(PsiMethodCallExpression methodCall) {
222       PsiReferenceExpression methodExpr = methodCall.getMethodExpression();
223       PsiExpression qualifier = methodExpr.getQualifierExpression();
224       if (!(qualifier instanceof PsiParenthesizedExpression)) return;
225       PsiExpression operand = ((PsiParenthesizedExpression)qualifier).getExpression();
226       if (!(operand instanceof PsiTypeCastExpression)) return;
227       PsiTypeCastExpression typeCast = (PsiTypeCastExpression)operand;
228       PsiExpression castOperand = typeCast.getOperand();
229       if (castOperand == null) return;
230
231       PsiType type = castOperand.getType();
232       if (type == null) return;
233       if (type instanceof PsiPrimitiveType) return;
234
235       final JavaResolveResult resolveResult = methodExpr.advancedResolve(false);
236       PsiMethod targetMethod = (PsiMethod)resolveResult.getElement();
237       if (targetMethod == null) return;
238       if (targetMethod.hasModifierProperty(PsiModifier.STATIC)) return;
239
240       try {
241         PsiManager manager = methodExpr.getManager();
242         PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
243
244         PsiMethodCallExpression newCall = (PsiMethodCallExpression)factory.createExpressionFromText(methodCall.getText(), methodCall);
245         PsiExpression newQualifier = newCall.getMethodExpression().getQualifierExpression();
246         PsiExpression newOperand = ((PsiTypeCastExpression)((PsiParenthesizedExpression)newQualifier).getExpression()).getOperand();
247         newQualifier.replace(newOperand);
248
249         final JavaResolveResult newResult = newCall.getMethodExpression().advancedResolve(false);
250         if (!newResult.isValidResult()) return;
251         final PsiMethod newTargetMethod = (PsiMethod)newResult.getElement();
252         final PsiType newReturnType = newResult.getSubstitutor().substitute(newTargetMethod.getReturnType());
253         final PsiType oldReturnType = resolveResult.getSubstitutor().substitute(targetMethod.getReturnType());
254         if (Comparing.equal(newReturnType, oldReturnType)) {
255           if (newTargetMethod.equals(targetMethod) ||
256               (newTargetMethod.getSignature(newResult.getSubstitutor()).equals(targetMethod.getSignature(resolveResult.getSubstitutor())) &&
257                !(newTargetMethod.isDeprecated() && !targetMethod.isDeprecated()) &&  // see SCR11555, SCR14559
258                areThrownExceptionsCompatible(targetMethod, newTargetMethod))) {
259             addToResults(typeCast);
260           }
261         }
262       }
263       catch (IncorrectOperationException ignore) { }
264     }
265
266     private static boolean areThrownExceptionsCompatible(final PsiMethod targetMethod, final PsiMethod newTargetMethod) {
267       final PsiClassType[] oldThrowsTypes = targetMethod.getThrowsList().getReferencedTypes();
268       final PsiClassType[] newThrowsTypes = newTargetMethod.getThrowsList().getReferencedTypes();
269       for (final PsiClassType throwsType : newThrowsTypes) {
270         if (!isExceptionThrown(throwsType, oldThrowsTypes)) return false;
271       }
272       return true;
273     }
274
275     private static boolean isExceptionThrown(PsiClassType exceptionType, PsiClassType[] thrownTypes) {
276       for (final PsiClassType type : thrownTypes) {
277         if (type.equals(exceptionType)) return true;
278       }
279       return false;
280     }
281
282     @Override public void visitNewExpression(PsiNewExpression expression) {
283       processCall(expression);
284       super.visitNewExpression(expression);
285     }
286
287     @Override
288     public void visitEnumConstant(PsiEnumConstant enumConstant) {
289       processCall(enumConstant);
290       super.visitEnumConstant(enumConstant);
291     }
292
293     @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
294       //expression.acceptChildren(this);
295     }
296
297     private void processCall(PsiCall expression){
298       PsiExpressionList argumentList = expression.getArgumentList();
299       if (argumentList == null) return;
300       PsiExpression[] args = argumentList.getExpressions();
301       PsiMethod oldMethod = expression.resolveMethod();
302       if (oldMethod == null) return;
303       PsiParameter[] parameters = oldMethod.getParameterList().getParameters();
304
305       try {
306         for (int i = 0; i < args.length; i++) {
307           final PsiExpression arg = deparenthesizeExpression(args[i]);
308           if (arg instanceof PsiTypeCastExpression) {
309             PsiTypeCastExpression cast = (PsiTypeCastExpression)arg;
310             if (i == args.length - 1 && args.length == parameters.length && parameters[i].isVarArgs()) {
311               //do not mark cast to resolve ambiguity for calling varargs method with inexact argument
312               continue;
313             }
314             PsiCall newCall = (PsiCall) expression.copy();
315             final PsiExpressionList argList = newCall.getArgumentList();
316             LOG.assertTrue(argList != null);
317             PsiExpression[] newArgs = argList.getExpressions();
318             PsiTypeCastExpression castExpression = (PsiTypeCastExpression) deparenthesizeExpression(newArgs[i]);
319             PsiExpression castOperand = castExpression.getOperand();
320             if (castOperand == null) return;
321             castExpression.replace(castOperand);
322             if (newCall instanceof PsiEnumConstant) {
323               // do this manually, because PsiEnumConstantImpl.resolveMethodGenerics() will assert (no containing class for the copy)
324               final PsiEnumConstant enumConstant = (PsiEnumConstant)expression;
325               PsiClass containingClass = enumConstant.getContainingClass();
326               final JavaPsiFacade facade = JavaPsiFacade.getInstance(enumConstant.getProject());
327               final PsiClassType type = facade.getElementFactory().createType(containingClass);
328               final JavaResolveResult newResult = facade.getResolveHelper().resolveConstructor(type, newCall.getArgumentList(), enumConstant);
329               if (oldMethod.equals(newResult.getElement()) && newResult.isValidResult()) {
330                 addToResults(cast);
331               }
332             } else {
333               final JavaResolveResult newResult = newCall.resolveMethodGenerics();
334               if (oldMethod.equals(newResult.getElement()) && newResult.isValidResult() &&
335                 Comparing.equal(((PsiCallExpression)newCall).getType(), ((PsiCallExpression)expression).getType())) {
336                 addToResults(cast);
337               }
338             }
339           }
340         }
341       }
342       catch (IncorrectOperationException e) {
343         return;
344       }
345
346       for (PsiExpression arg : args) {
347         if (arg instanceof PsiTypeCastExpression) {
348           PsiExpression castOperand = ((PsiTypeCastExpression)arg).getOperand();
349           if (castOperand != null) {
350             castOperand.accept(this);
351           }
352         }
353         else {
354           arg.accept(this);
355         }
356       }
357     }
358
359     @Override public void visitTypeCastExpression(PsiTypeCastExpression typeCast) {
360       PsiExpression operand = typeCast.getOperand();
361       if (operand == null) return;
362
363       PsiElement expr = deparenthesizeExpression(operand);
364
365       if (expr instanceof PsiTypeCastExpression) {
366         PsiTypeElement typeElement = ((PsiTypeCastExpression)expr).getCastType();
367         if (typeElement == null) return;
368         PsiType castType = typeElement.getType();
369         final PsiExpression innerOperand = ((PsiTypeCastExpression)expr).getOperand();
370         final PsiType operandType = innerOperand != null ? innerOperand.getType() : null;
371         final PsiType topCastType = typeCast.getType();
372         if (!(castType instanceof PsiPrimitiveType)) {
373           if (operandType != null && topCastType != null && TypeConversionUtil.areTypesConvertible(operandType, topCastType)) {
374             addToResults((PsiTypeCastExpression)expr);
375           }
376         } else if (PsiPrimitiveType.getUnboxedType(operandType) == topCastType) {
377           addToResults((PsiTypeCastExpression)expr);
378         }
379       }
380       else {
381         PsiElement parent = typeCast.getParent();
382         if (parent instanceof PsiConditionalExpression) {
383           //branches need to be of the same type
384           if (!Comparing.equal(operand.getType(), ((PsiConditionalExpression)parent).getType())) {
385             if (!PsiUtil.isLanguageLevel5OrHigher(typeCast)) {
386               return;
387             }
388             if (!checkResolveAfterRemoveCast(parent)) return;
389           }
390         } else if (parent instanceof PsiSynchronizedStatement && (expr instanceof PsiExpression && ((PsiExpression)expr).getType() instanceof PsiPrimitiveType)) {
391           return;
392         }
393         processAlreadyHasTypeCast(typeCast);
394       }
395       super.visitTypeCastExpression(typeCast);
396     }
397
398     private static boolean checkResolveAfterRemoveCast(PsiElement parent) {
399       PsiElement grandPa = parent.getParent();
400       if (grandPa instanceof PsiExpressionList) {
401         PsiExpression[] expressions = ((PsiExpressionList)grandPa).getExpressions();
402         int idx = ArrayUtil.find(expressions, parent);
403         PsiElement grandGrandPa = grandPa.getParent();
404         if (grandGrandPa instanceof PsiCall) {
405           PsiElement resolve = ((PsiCall)grandGrandPa).resolveMethod();
406           if (resolve instanceof PsiMethod) {
407             PsiCall expression = (PsiCall)grandGrandPa.copy();
408             PsiExpressionList argumentList = expression.getArgumentList();
409             LOG.assertTrue(argumentList != null);
410             PsiExpression toReplace = argumentList.getExpressions()[idx];
411             if (toReplace instanceof PsiConditionalExpression) {
412               PsiExpression thenExpression = ((PsiConditionalExpression)toReplace).getThenExpression();
413               PsiExpression elseExpression = ((PsiConditionalExpression)toReplace).getElseExpression();
414               if (thenExpression instanceof PsiTypeCastExpression) {
415                 final PsiExpression thenOperand = ((PsiTypeCastExpression)thenExpression).getOperand();
416                 if (thenOperand != null) {
417                   thenExpression.replace(thenOperand);
418                 }
419               } else if (elseExpression instanceof PsiTypeCastExpression) {
420                 final PsiExpression elseOperand = ((PsiTypeCastExpression)elseExpression).getOperand();
421                 if (elseOperand != null) {
422                   elseExpression.replace(elseOperand);
423                 }
424               }
425             }
426             if (expression.resolveMethod() != resolve) {
427               return false;
428             }
429           }
430         }
431       }
432       return true;
433     }
434
435     private void processAlreadyHasTypeCast(PsiTypeCastExpression typeCast){
436       PsiElement parent = typeCast.getParent();
437       while(parent instanceof PsiParenthesizedExpression) parent = parent.getParent();
438       if (parent instanceof PsiExpressionList) return; // do not replace in arg lists - should be handled by parent
439       if (parent instanceof PsiReturnStatement) return;
440       if (parent instanceof PsiTypeCastExpression) return;
441
442       if (isTypeCastSemantical(typeCast)) return;
443
444       PsiTypeElement typeElement = typeCast.getCastType();
445       if (typeElement == null) return;
446       PsiType castTo = typeElement.getType();
447       PsiType opType = typeCast.getOperand().getType();
448       if (opType == null) return;
449       if (parent instanceof PsiReferenceExpression) {
450         if (castTo instanceof PsiClassType && opType instanceof PsiPrimitiveType) return; //explicit boxing
451         //Check accessibility
452         if (opType instanceof PsiClassType) {
453           final PsiReferenceExpression refExpression = (PsiReferenceExpression)parent;
454           PsiElement element = refExpression.resolve();
455           if (!(element instanceof PsiMember)) return;
456           PsiClass accessClass = ((PsiClassType)opType).resolve();
457           if (accessClass == null) return;
458           if (!JavaPsiFacade.getInstance(parent.getProject()).getResolveHelper().isAccessible((PsiMember)element, typeCast, accessClass)) return;
459           if (!isCastRedundantInRefExpression(refExpression, typeCast.getOperand())) return;
460         }
461       }
462
463       if (someWhereAtTheLeftSideOfAssignment(parent)) {
464         if (TypeConversionUtil.isAssignable(opType, castTo, false)) {
465           addToResults(typeCast);
466         }
467       }
468       else {
469         if (TypeConversionUtil.isAssignable(castTo, opType, false)) {
470           addToResults(typeCast);
471         }
472       }
473     }
474
475     private static boolean someWhereAtTheLeftSideOfAssignment(PsiElement element) {
476       PsiAssignmentExpression assignment = PsiTreeUtil.getParentOfType(element, PsiAssignmentExpression.class, false, PsiMember.class);
477       if (assignment == null) return false;
478       PsiExpression lExpression = assignment.getLExpression();
479       return PsiTreeUtil.isAncestor(lExpression, element, false);
480     }
481   }
482
483   private static boolean isCastRedundantInRefExpression (final PsiReferenceExpression refExpression, final PsiExpression castOperand) {
484     final PsiElement resolved = refExpression.resolve();
485     final Ref<Boolean> result = new Ref<Boolean>(Boolean.FALSE);
486     CodeStyleManager.getInstance(refExpression.getProject()).performActionWithFormatterDisabled(new Runnable() {
487       @Override
488       public void run() {
489         try {
490           final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(refExpression.getProject()).getElementFactory();
491           final PsiExpression copyExpression = elementFactory.createExpressionFromText(refExpression.getText(), refExpression);
492           if (copyExpression instanceof PsiReferenceExpression) {
493             final PsiReferenceExpression copy = (PsiReferenceExpression)copyExpression;
494             final PsiExpression qualifier = copy.getQualifierExpression();
495             if (qualifier != null) {
496               qualifier.replace(castOperand);
497               result.set(Boolean.valueOf(copy.resolve() == resolved));
498             }
499           }
500         }
501         catch (IncorrectOperationException ignore) { }
502       }
503     });
504     return result.get().booleanValue();
505   }
506
507   public static boolean isTypeCastSemantical(PsiTypeCastExpression typeCast) {
508     PsiExpression operand = typeCast.getOperand();
509     if (operand == null) return false;
510
511     if (isInPolymorphicCall(typeCast)) return true;
512
513     PsiType opType = operand.getType();
514     PsiTypeElement typeElement = typeCast.getCastType();
515     if (typeElement == null) return false;
516
517     PsiType castType = typeElement.getType();
518     if (castType instanceof PsiPrimitiveType) {
519       if (opType instanceof PsiPrimitiveType) {
520         return !opType.equals(castType); // let's suppose all not equal primitive casts are necessary
521       }
522       final PsiPrimitiveType unboxedOpType = PsiPrimitiveType.getUnboxedType(opType);
523       if (unboxedOpType != null && !unboxedOpType.equals(castType) ) {
524         return true;
525       }
526     }
527     else if (castType instanceof PsiClassType && ((PsiClassType)castType).hasParameters()) {
528       if (opType instanceof PsiClassType && ((PsiClassType)opType).isRaw()) return true;
529     } else if (castType instanceof PsiClassType && ((PsiClassType)castType).isRaw()) {
530       if (opType instanceof PsiClassType && ((PsiClassType)opType).hasParameters()) return true;
531     }
532
533     PsiElement parent = typeCast.getParent();
534     while(parent instanceof PsiParenthesizedExpression) parent = parent.getParent();
535
536     if (parent instanceof PsiBinaryExpression) {
537       PsiBinaryExpression expression = (PsiBinaryExpression)parent;
538       PsiExpression firstOperand = expression.getLOperand();
539       PsiExpression otherOperand = expression.getROperand();
540       if (PsiTreeUtil.isAncestor(otherOperand, typeCast, false)) {
541         PsiExpression temp = otherOperand;
542         otherOperand = firstOperand;
543         firstOperand = temp;
544       }
545       if (firstOperand != null && otherOperand != null && wrapperCastChangeSemantics(firstOperand, otherOperand, operand)) {
546         return true;
547       }
548     }
549     return false;
550   }
551
552   private static boolean wrapperCastChangeSemantics(PsiExpression operand, PsiExpression otherOperand, PsiExpression toCast) {
553     boolean isPrimitiveComparisonWithCast = TypeConversionUtil.isPrimitiveAndNotNull(operand.getType()) ||
554                                             TypeConversionUtil.isPrimitiveAndNotNull(otherOperand.getType());
555     boolean isPrimitiveComparisonWithoutCast = TypeConversionUtil.isPrimitiveAndNotNull(toCast.getType()) ||
556                                                TypeConversionUtil.isPrimitiveAndNotNull(otherOperand.getType());
557     // wrapper casted to primitive vs wrapper comparison
558     return isPrimitiveComparisonWithCast != isPrimitiveComparisonWithoutCast;
559   }
560
561   // see http://download.java.net/jdk7/docs/api/java/lang/invoke/MethodHandle.html#sigpoly
562   public static boolean isInPolymorphicCall(final PsiTypeCastExpression typeCast) {
563     if (!PsiUtil.isLanguageLevel7OrHigher(typeCast)) return false;
564
565     // return type
566     final PsiExpression operand = typeCast.getOperand();
567     if (operand instanceof PsiMethodCallExpression) {
568       if (isPolymorphicMethod((PsiMethodCallExpression)operand)) return true;
569     }
570
571     // argument type
572     final PsiElement exprList = typeCast.getParent();
573     if (exprList instanceof PsiExpressionList) {
574       final PsiElement methodCall = exprList.getParent();
575       if (methodCall instanceof PsiMethodCallExpression) {
576         if (isPolymorphicMethod((PsiMethodCallExpression)methodCall)) return true;
577       }
578     }
579
580     return false;
581   }
582
583   private static boolean isPolymorphicMethod(PsiMethodCallExpression expression) {
584     final PsiElement method = expression.getMethodExpression().resolve();
585     return method instanceof PsiMethod &&
586            AnnotationUtil.isAnnotated((PsiMethod)method, CommonClassNames.JAVA_LANG_INVOKE_MH_POLYMORPHIC, false, true);
587   }
588 }