assignability to primitive: check type parameter bounds if applicable (IDEA-155551)
[idea/community.git] / java / java-psi-api / src / com / intellij / psi / util / TypeConversionUtil.java
1 /*
2  * Copyright 2000-2014 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.openapi.diagnostic.Logger;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.openapi.roots.ProjectRootModificationTracker;
21 import com.intellij.openapi.util.*;
22 import com.intellij.pom.java.LanguageLevel;
23 import com.intellij.psi.*;
24 import com.intellij.psi.search.GlobalSearchScope;
25 import com.intellij.psi.tree.IElementType;
26 import com.intellij.util.Processor;
27 import com.intellij.util.containers.ContainerUtil;
28 import gnu.trove.THashMap;
29 import gnu.trove.THashSet;
30 import gnu.trove.TObjectIntHashMap;
31 import org.jetbrains.annotations.Contract;
32 import org.jetbrains.annotations.NonNls;
33 import org.jetbrains.annotations.NotNull;
34 import org.jetbrains.annotations.Nullable;
35
36 import java.util.*;
37
38 import static com.intellij.psi.CommonClassNames.JAVA_LANG_STRING;
39
40 public class TypeConversionUtil {
41   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.util.TypeConversionUtil");
42
43   private static final boolean[][] IS_ASSIGNABLE_BIT_SET = {
44     {true, true, false, true, true, true, true}, // byte
45     {false, true, false, true, true, true, true}, // short
46     {false, false, true, true, true, true, true}, // char
47     {false, false, false, true, true, true, true}, // int
48     {false, false, false, false, true, true, true}, // long
49     {false, false, false, false, false, true, true}, // float
50     {false, false, false, false, false, false, true}, // double
51   };
52
53   private static final TObjectIntHashMap<PsiType> TYPE_TO_RANK_MAP = new TObjectIntHashMap<PsiType>();
54
55   public static final int BYTE_RANK = 1;
56   public static final int SHORT_RANK = 2;
57   public static final int CHAR_RANK = 3;
58   public static final int INT_RANK = 4;
59   public static final int LONG_RANK = 5;
60   private static final int FLOAT_RANK = 6;
61   private static final int DOUBLE_RANK = 7;
62   private static final int BOOL_RANK = 10;
63   private static final int STRING_RANK = 100;
64   private static final int MAX_NUMERIC_RANK = DOUBLE_RANK;
65   public static final PsiType NULL_TYPE = new PsiEllipsisType(PsiType.NULL){
66     @Override
67     public boolean isValid() {
68       return true;
69     }
70
71     @NotNull
72     @Override
73     @NonNls
74     public String getPresentableText() {
75       return "FAKE TYPE";
76     }
77   };
78   private static final Key<PsiElement> ORIGINAL_CONTEXT = Key.create("ORIGINAL_CONTEXT");
79
80   static {
81     TYPE_TO_RANK_MAP.put(PsiType.BYTE, BYTE_RANK);
82     TYPE_TO_RANK_MAP.put(PsiType.SHORT, SHORT_RANK);
83     TYPE_TO_RANK_MAP.put(PsiType.CHAR, CHAR_RANK);
84     TYPE_TO_RANK_MAP.put(PsiType.INT, INT_RANK);
85     TYPE_TO_RANK_MAP.put(PsiType.LONG, LONG_RANK);
86     TYPE_TO_RANK_MAP.put(PsiType.FLOAT, FLOAT_RANK);
87     TYPE_TO_RANK_MAP.put(PsiType.DOUBLE, DOUBLE_RANK);
88     TYPE_TO_RANK_MAP.put(PsiType.BOOLEAN, BOOL_RANK);
89   }
90
91   private TypeConversionUtil() { }
92
93   /**
94    * @return true if fromType can be casted to toType
95    */
96   public static boolean areTypesConvertible(@NotNull PsiType fromType, @NotNull PsiType toType) {
97     return areTypesConvertible(fromType, toType, null);
98   }
99
100   /**
101    * @return true if fromType can be casted to toType
102    */
103   public static boolean areTypesConvertible(@NotNull PsiType fromType, @NotNull PsiType toType, @Nullable LanguageLevel languageLevel) {
104     if (fromType == toType) return true;
105     final boolean fromIsPrimitive = isPrimitiveAndNotNull(fromType);
106     final boolean toIsPrimitive = isPrimitiveAndNotNull(toType);
107     if (fromIsPrimitive || toIsPrimitive) {
108       if (isVoidType(fromType) || isVoidType(toType)) return false;
109       final int fromTypeRank = getTypeRank(fromType);
110       final int toTypeRank = getTypeRank(toType);
111       if (!toIsPrimitive) {
112         if (fromTypeRank == toTypeRank) return true;
113         if (toType instanceof PsiIntersectionType) {
114           for (PsiType type : ((PsiIntersectionType)toType).getConjuncts()) {
115             if (!areTypesConvertible(fromType, type)) return false;
116           }
117           return true;
118         }
119         // JLS 5.5: A value of a primitive type can be cast to a reference type by boxing conversion(see 5.1.7)
120         if (!(toType instanceof PsiClassType)) return false;
121         PsiClass toClass = ((PsiClassType)toType).resolve();
122         if (toClass == null || toClass instanceof PsiTypeParameter) return false;
123         PsiClassType boxedType = ((PsiPrimitiveType)fromType).getBoxedType(toClass.getManager(), toType.getResolveScope());
124         return boxedType != null && areTypesConvertible(boxedType, toType);
125       }
126       if (!fromIsPrimitive) {
127         // 5.5. Casting Contexts
128         if ((fromTypeRank == SHORT_RANK || fromTypeRank == BYTE_RANK) && toTypeRank == CHAR_RANK) return false;
129
130         if (fromType instanceof PsiClassType) {
131           if (languageLevel == null) {
132             languageLevel = ((PsiClassType)fromType).getLanguageLevel();
133           }
134
135           if (languageLevel.isAtLeast(LanguageLevel.JDK_1_7)) {
136             final PsiClassType classType = (PsiClassType)fromType;
137             final PsiClass psiClass = classType.resolve();
138             if (psiClass == null) return false;
139             final PsiClassType boxedType = ((PsiPrimitiveType)toType).getBoxedType(psiClass.getManager(), psiClass.getResolveScope());
140             if (boxedType != null && isNarrowingReferenceConversionAllowed(fromType, boxedType)) {
141               return true;
142             }
143           }
144         }
145         return fromTypeRank == toTypeRank ||
146                fromTypeRank <= MAX_NUMERIC_RANK && toTypeRank <= MAX_NUMERIC_RANK && fromTypeRank < toTypeRank;
147       }
148       return fromTypeRank == toTypeRank ||
149              fromTypeRank <= MAX_NUMERIC_RANK && toTypeRank <= MAX_NUMERIC_RANK;
150     }
151
152     //type can be casted via widening reference conversion
153     if (isAssignable(toType, fromType)) return true;
154
155     if (isNullType(fromType) || isNullType(toType)) return true;
156
157     // or narrowing reference conversion
158     return isNarrowingReferenceConversionAllowed(fromType, toType);
159   }
160
161   /**
162    * see JLS 5.1.5, JLS3 5.1.6
163    */
164   private static boolean isNarrowingReferenceConversionAllowed(@NotNull PsiType fromType, @NotNull PsiType toType) {
165     if (toType instanceof PsiPrimitiveType || fromType instanceof PsiPrimitiveType) return fromType.equals(toType);
166     //Done with primitives
167     if (toType instanceof PsiDiamondType || fromType instanceof PsiDiamondType) return false;
168     if (toType instanceof PsiArrayType && !(fromType instanceof PsiArrayType)) {
169       if (fromType instanceof PsiClassType) {
170         final PsiClass resolved = ((PsiClassType)fromType).resolve();
171         if (resolved instanceof PsiTypeParameter) {
172           for (final PsiClassType boundType : resolved.getExtendsListTypes()) {
173             if (!isNarrowingReferenceConversionAllowed(boundType, toType)) return false;
174           }
175           return true;
176         }
177       }
178       if (fromType instanceof PsiCapturedWildcardType) {
179         return isNarrowingReferenceConversionAllowed(((PsiCapturedWildcardType)fromType).getUpperBound(), toType);
180       }
181       return isAssignable(fromType, toType);
182     }
183     if (fromType instanceof PsiArrayType) {
184       if (toType instanceof PsiClassType) {
185         final PsiClass resolved = ((PsiClassType)toType).resolve();
186         if (resolved instanceof PsiTypeParameter) {
187           for (final PsiClassType boundType : resolved.getExtendsListTypes()) {
188             if (!areTypesConvertible(fromType, boundType)) return false;
189           }
190           return true;
191         }
192       }
193       return toType instanceof PsiArrayType
194              && isNarrowingReferenceConversionAllowed(((PsiArrayType)fromType).getComponentType(),
195                                                       ((PsiArrayType)toType).getComponentType());
196     }
197     //Done with array types
198
199     if (fromType instanceof PsiIntersectionType) {
200       final PsiType[] conjuncts = ((PsiIntersectionType)fromType).getConjuncts();
201       for (PsiType conjunct : conjuncts) {
202         if (isNarrowingReferenceConversionAllowed(conjunct, toType)) return true;
203       }
204       return false;
205     }
206     else if (toType instanceof PsiIntersectionType) {
207       if (fromType instanceof PsiClassType && ((PsiClassType)fromType).getLanguageLevel().isAtLeast(LanguageLevel.JDK_1_8)) {
208         for (PsiType conjunct : ((PsiIntersectionType)toType).getConjuncts()) {
209           if (!isNarrowingReferenceConversionAllowed(fromType, conjunct)) return false;
210         }
211         return true;
212       }
213       return false;
214     }
215
216     if (fromType instanceof PsiDisjunctionType) {
217       return isNarrowingReferenceConversionAllowed(((PsiDisjunctionType)fromType).getLeastUpperBound(), toType);
218     }
219     if (toType instanceof PsiDisjunctionType) {
220       return false;
221     }
222
223     if (fromType instanceof PsiWildcardType) {
224       final PsiWildcardType fromWildcard = (PsiWildcardType)fromType;
225       final PsiType bound = fromWildcard.getBound();
226       if (bound == null) return true;
227       if (fromWildcard.isSuper()) {
228         return isAssignable(toType, bound);
229       }
230       return isNarrowingReferenceConversionAllowed(bound, toType);
231     }
232     if (toType instanceof PsiWildcardType) {
233       final PsiWildcardType toWildcard = (PsiWildcardType)toType;
234       if (toWildcard.isSuper()) return false;
235       final PsiType bound = toWildcard.getBound();
236       return bound == null || isNarrowingReferenceConversionAllowed(fromType, bound);
237     }
238
239     if (toType instanceof PsiCapturedWildcardType) {
240       return isNarrowingReferenceConversionAllowed(fromType, ((PsiCapturedWildcardType)toType).getUpperBound());
241     }
242     if (fromType instanceof PsiCapturedWildcardType) {
243       return isNarrowingReferenceConversionAllowed(((PsiCapturedWildcardType)fromType).getUpperBound(), toType);
244     }
245
246     if (isAssignable(fromType, toType)) return true;
247
248     if (!(fromType instanceof PsiClassType) || !(toType instanceof PsiClassType)) return false;
249     PsiClassType fromClassType = (PsiClassType)fromType;
250     PsiClassType toClassType = (PsiClassType)toType;
251
252     PsiClassType.ClassResolveResult fromResult = fromClassType.resolveGenerics();
253     final PsiClass fromClass = fromResult.getElement();
254     if (fromClass == null) return false;
255     if (fromClass instanceof PsiTypeParameter) return isNarrowingReferenceConversionAllowed(obtainSafeSuperType((PsiTypeParameter)fromClass), toType);
256
257     PsiClassType.ClassResolveResult toResult = toClassType.resolveGenerics();
258     final PsiClass toClass = toResult.getElement();
259     if (toClass == null) return false;
260     if (toClass instanceof PsiTypeParameter) return isNarrowingReferenceConversionAllowed(fromType, obtainSafeSuperType((PsiTypeParameter)toClass));
261     //Done with type parameters
262
263     PsiManager manager = fromClass.getManager();
264     final LanguageLevel languageLevel = toClassType.getLanguageLevel();
265     if (!fromClass.isInterface()) {
266       if (toClass.isInterface()) {
267         return (!fromClass.hasModifierProperty(PsiModifier.FINAL) || fromClass.isInheritor(toClass, true))&&
268                checkSuperTypesWithDifferentTypeArguments(toResult, fromClass, manager, fromResult.getSubstitutor(), null, languageLevel);
269       }
270       else {
271         if (manager.areElementsEquivalent(fromClass, toClass)) {
272           return areSameParameterTypes(fromClassType, toClassType);
273         }
274
275         if (toClass.isInheritor(fromClass, true)) {
276           return checkSuperTypesWithDifferentTypeArguments(fromResult, toClass, manager, toResult.getSubstitutor(), null, languageLevel);
277         }
278         else if (fromClass.isInheritor(toClass, true)) {
279           return checkSuperTypesWithDifferentTypeArguments(toResult, fromClass, manager, fromResult.getSubstitutor(), null, languageLevel);
280         }
281
282         return false;
283       }
284     }
285     else if (!toClass.isInterface()) {
286       if (!toClass.hasModifierProperty(PsiModifier.FINAL)) {
287         return checkSuperTypesWithDifferentTypeArguments(fromResult, toClass, manager, toResult.getSubstitutor(), null, languageLevel);
288       }
289       else {
290         PsiSubstitutor toSubstitutor = getMaybeSuperClassSubstitutor(fromClass, toClass, toResult.getSubstitutor(), null);
291         return toSubstitutor != null && areSameArgumentTypes(fromClass, fromResult.getSubstitutor(), toSubstitutor);
292       }
293     }
294     else if (languageLevel.compareTo(LanguageLevel.JDK_1_5) < 0) {
295       //In jls2 check for method in both interfaces with the same signature but different return types.
296       Collection<HierarchicalMethodSignature> fromClassMethodSignatures = fromClass.getVisibleSignatures();
297       Collection<HierarchicalMethodSignature> toClassMethodSignatures = toClass.getVisibleSignatures();
298
299       for (HierarchicalMethodSignature fromMethodSignature : fromClassMethodSignatures) {
300         for (HierarchicalMethodSignature toMethodSignature : toClassMethodSignatures) {
301           if (fromMethodSignature.equals(toMethodSignature)) {
302             final PsiType fromClassReturnType = fromMethodSignature.getMethod().getReturnType();
303             final PsiType toClassReturnType = toMethodSignature.getMethod().getReturnType();
304             if (fromClassReturnType != null
305                 && toClassReturnType != null
306                 && !fromClassReturnType.equals(toClassReturnType)) {
307               return false;
308             }
309           }
310         }
311       }
312       return true;
313     }
314     else {
315       //In jls3 check for super interface with distinct type arguments
316       PsiClassType.ClassResolveResult baseResult;
317       PsiClass derived;
318       PsiSubstitutor derivedSubstitutor;
319       if (toClass.isInheritor(fromClass, true)) {
320         baseResult = fromResult;
321         derived = toClass;
322         derivedSubstitutor = toResult.getSubstitutor();
323       }
324       else {
325         baseResult = toResult;
326         derived = fromClass;
327         derivedSubstitutor = fromResult.getSubstitutor();
328       }
329       return checkSuperTypesWithDifferentTypeArguments(baseResult, derived, manager, derivedSubstitutor, null, languageLevel);
330     }
331   }
332
333   @NotNull
334   private static PsiClassType obtainSafeSuperType(@NotNull PsiTypeParameter typeParameter) {
335     final PsiClassType superType = typeParameter.getSuperTypes()[0];
336     final PsiClassType.ClassResolveResult result = superType.resolveGenerics();
337     final PsiClass superClass = result.getElement();
338     if (superClass != null) {
339       final PsiSubstitutor substitutor = result.getSubstitutor().put(typeParameter, null);
340       return JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(superClass, substitutor);
341     }
342     return superType;
343   }
344
345   private static boolean checkSuperTypesWithDifferentTypeArguments(@NotNull PsiClassType.ClassResolveResult baseResult,
346                                                                    @NotNull PsiClass derived,
347                                                                    @NotNull PsiManager manager,
348                                                                    @NotNull PsiSubstitutor derivedSubstitutor,
349                                                                    Set<PsiClass> visited,
350                                                                    @NotNull LanguageLevel languageLevel) {
351     if (visited != null && visited.contains(derived)) return true;
352
353     if (languageLevel.compareTo(LanguageLevel.JDK_1_5) < 0) return true;
354     PsiClass base = baseResult.getElement();
355     PsiClass[] supers = derived.getSupers();
356     if (manager.areElementsEquivalent(base, derived)) {
357       derivedSubstitutor = getSuperClassSubstitutor(derived, derived, derivedSubstitutor);
358       return areSameArgumentTypes(derived, baseResult.getSubstitutor(), derivedSubstitutor, 1);
359     }
360     else {
361       PsiSubstitutor baseSubstitutor = getMaybeSuperClassSubstitutor(derived, base, baseResult.getSubstitutor(), null);
362       if (baseSubstitutor != null) {
363         derivedSubstitutor = getSuperClassSubstitutor(derived, derived, derivedSubstitutor);
364         if (!areSameArgumentTypes(derived, baseSubstitutor, derivedSubstitutor)) return false;
365       }
366     }
367
368     if (visited == null) visited = new THashSet<PsiClass>();
369     visited.add(derived);
370     for (PsiClass aSuper : supers) {
371       PsiSubstitutor s = getSuperClassSubstitutor(aSuper, derived, derivedSubstitutor);
372       if (!checkSuperTypesWithDifferentTypeArguments(baseResult, aSuper, manager, s, visited, languageLevel)) return false;
373     }
374
375     return true;
376   }
377
378   private static boolean areSameParameterTypes(@NotNull PsiClassType type1, @NotNull PsiClassType type2) {
379     PsiClassType.ClassResolveResult resolveResult1 = type1.resolveGenerics();
380     PsiClassType.ClassResolveResult resolveResult2 = type2.resolveGenerics();
381     final PsiClass aClass = resolveResult1.getElement();
382     final PsiClass bClass = resolveResult2.getElement();
383     return aClass != null &&
384            bClass != null &&
385            aClass.getManager().areElementsEquivalent(aClass, bClass) &&
386            areSameArgumentTypes(aClass, resolveResult1.getSubstitutor(), resolveResult2.getSubstitutor(), 1);
387   }
388
389   private static boolean areSameArgumentTypes(@NotNull PsiClass aClass, @NotNull PsiSubstitutor substitutor1, @NotNull PsiSubstitutor substitutor2) {
390     return areSameArgumentTypes(aClass, substitutor1, substitutor2, 0);
391   }
392
393   private static boolean areSameArgumentTypes(@NotNull PsiClass aClass,
394                                               @NotNull PsiSubstitutor substitutor1,
395                                               @NotNull PsiSubstitutor substitutor2,
396                                               int level) {
397     for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(aClass)) {
398       PsiType typeArg1 = substitutor1.substitute(typeParameter);
399       PsiType typeArg2 = substitutor2.substitute(typeParameter);
400       if (typeArg1 == null || typeArg2 == null) return true;
401       if (TypesDistinctProver.provablyDistinct(typeArg1, typeArg2, level)) return false;
402     }
403
404     return true;
405   }
406
407   public static boolean isPrimitiveAndNotNull(PsiType type) {
408     if (type instanceof PsiCapturedWildcardType) {
409       return isPrimitiveAndNotNull(((PsiCapturedWildcardType)type).getUpperBound());
410     }
411     return type instanceof PsiPrimitiveType && !isNullType(type);
412   }
413
414   public static boolean isEnumType(PsiType type) {
415     if (type instanceof PsiCapturedWildcardType) {
416       return isEnumType(((PsiCapturedWildcardType)type).getUpperBound());
417     }
418     if (type instanceof PsiClassType) {
419       final PsiClass psiClass = ((PsiClassType)type).resolve();
420       return psiClass != null && psiClass.isEnum();
421     }
422     return false;
423   }
424
425   public static boolean isNullType(PsiType type) {
426     return PsiType.NULL.equals(type);
427   }
428
429   public static boolean isFloatOrDoubleType(PsiType type) {
430     return isFloatType(type) || isDoubleType(type);
431   }
432   public static boolean isDoubleType(PsiType type) {
433     if (type instanceof PsiCapturedWildcardType) {
434       return isDoubleType(((PsiCapturedWildcardType)type).getUpperBound());
435     }
436     return PsiType.DOUBLE.equals(type) || PsiType.DOUBLE.equals(PsiPrimitiveType.getUnboxedType(type));
437   }
438
439   public static boolean isFloatType(PsiType type) {
440     if (type instanceof PsiCapturedWildcardType) {
441       return isFloatType(((PsiCapturedWildcardType)type).getUpperBound());
442     }
443     return PsiType.FLOAT.equals(type) || PsiType.FLOAT.equals(PsiPrimitiveType.getUnboxedType(type));
444   }
445
446   public static boolean isLongType(PsiType type) {
447     if (type instanceof PsiCapturedWildcardType) {
448       return isLongType(((PsiCapturedWildcardType)type).getUpperBound());
449     }
450     return PsiType.LONG.equals(type) || PsiType.LONG.equals(PsiPrimitiveType.getUnboxedType(type));
451   }
452
453   public static boolean isVoidType(PsiType type) {
454     return PsiType.VOID.equals(type);
455   }
456
457   public static boolean isBooleanType(@Nullable PsiType type) {
458     if (type instanceof PsiCapturedWildcardType) {
459       return isBooleanType(((PsiCapturedWildcardType)type).getUpperBound());
460     }
461     return PsiType.BOOLEAN.equals(type) || PsiType.BOOLEAN.equals(PsiPrimitiveType.getUnboxedType(type));
462   }
463
464   public static boolean isNumericType(int typeRank) {
465     return typeRank <= MAX_NUMERIC_RANK;
466   }
467   public static boolean isNumericType(PsiType type) {
468     return type != null && isNumericType(getTypeRank(type));
469   }
470
471   /**
472    * @return 1..MAX_NUMERIC_TYPE if type is primitive numeric type,
473    *         BOOL_TYPE for boolean,
474    *         STRING_TYPE for String,
475    *         Integer.MAX_VALUE for others
476    */
477   public static int getTypeRank(@NotNull PsiType type) {
478     if (type instanceof PsiCapturedWildcardType) {
479       type = ((PsiCapturedWildcardType)type).getUpperBound();
480     }
481     PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(type);
482     if (unboxedType != null) {
483       type = unboxedType;
484     }
485
486     int rank = TYPE_TO_RANK_MAP.get(type);
487     if (rank != 0) return rank;
488     if (type.equalsToText(JAVA_LANG_STRING)) return STRING_RANK;
489     return Integer.MAX_VALUE;
490   }
491
492   /**
493    * @param tokenType JavaTokenType enumeration
494    * @param strict    true if operator result type should be convertible to the left operand
495    * @return true if lOperand operator rOperand expression is syntactically correct
496    */
497   public static boolean isBinaryOperatorApplicable(IElementType tokenType,
498                                                    PsiExpression lOperand,
499                                                    PsiExpression rOperand,
500                                                    boolean strict) {
501     if (lOperand == null || rOperand == null) return true;
502     final PsiType ltype = lOperand.getType();
503     final PsiType rtype = rOperand.getType();
504     return isBinaryOperatorApplicable(tokenType, ltype, rtype, strict);
505   }
506
507   public static boolean isBinaryOperatorApplicable(final IElementType tokenType, final PsiType ltype, final PsiType rtype, final boolean strict) {
508     if (ltype == null || rtype == null) return true;
509     int resultTypeRank = BOOL_RANK;
510     boolean isApplicable = false;
511     final int ltypeRank = getTypeRank(ltype);
512     final int rtypeRank = getTypeRank(rtype);
513     Label:
514     if (tokenType == JavaTokenType.LT || tokenType == JavaTokenType.LE || tokenType == JavaTokenType.GT || tokenType == JavaTokenType.GE) {
515       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
516         isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK;
517       }
518     }
519     else if (tokenType == JavaTokenType.EQEQ || tokenType == JavaTokenType.NE) {
520       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype) &&
521           (isPrimitiveAndNotNull(ltype) || isPrimitiveAndNotNull(rtype))) {
522         isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK
523                        || ltypeRank == BOOL_RANK && rtypeRank == BOOL_RANK;
524       }
525       else {
526         if (isPrimitiveAndNotNull(ltype)) {
527           if (rtype instanceof PsiClassType) {
528             final LanguageLevel languageLevel = ((PsiClassType)rtype).getLanguageLevel();
529             if (languageLevel.isAtLeast(LanguageLevel.JDK_1_5) &&
530                 !languageLevel.isAtLeast(LanguageLevel.JDK_1_8) &&
531                 areTypesConvertible(ltype, rtype)) {
532               return true;
533             }
534           }
535           return false;
536         }
537         if (isPrimitiveAndNotNull(rtype)) {
538           if (ltype instanceof PsiClassType) {
539             final LanguageLevel level = ((PsiClassType)ltype).getLanguageLevel();
540             if (level.isAtLeast(LanguageLevel.JDK_1_7) && !level.isAtLeast(LanguageLevel.JDK_1_8) && areTypesConvertible(rtype, ltype)) {
541               return true;
542             }
543           }
544           return false;
545         }
546         isApplicable = areTypesConvertible(ltype, rtype) || areTypesConvertible(rtype, ltype);
547       }
548     }
549     else if (tokenType == JavaTokenType.PLUS) {
550       if (ltype.equalsToText(JAVA_LANG_STRING)) {
551         isApplicable = !isVoidType(rtype);
552         resultTypeRank = STRING_RANK;
553         break Label;
554       }
555       else if (rtype.equalsToText(JAVA_LANG_STRING)) {
556         isApplicable = !isVoidType(ltype);
557         resultTypeRank = STRING_RANK;
558         break Label;
559       }
560       //fallthrough
561
562
563
564
565       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
566         resultTypeRank = Math.max(ltypeRank, rtypeRank);
567         isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK;
568       }
569     }
570     else if (tokenType == JavaTokenType.ASTERISK || tokenType == JavaTokenType.DIV || tokenType == JavaTokenType.PERC ||
571              tokenType == JavaTokenType.MINUS) {
572       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
573         resultTypeRank = Math.max(ltypeRank, rtypeRank);
574         isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK;
575       }
576     }
577     else if (tokenType == JavaTokenType.LTLT || tokenType == JavaTokenType.GTGT || tokenType == JavaTokenType.GTGTGT) {
578       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
579         isApplicable = ltypeRank <= LONG_RANK && rtypeRank <= LONG_RANK;
580         resultTypeRank = INT_RANK;
581       }
582     }
583     else if (tokenType == JavaTokenType.AND || tokenType == JavaTokenType.OR || tokenType == JavaTokenType.XOR) {
584       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
585         isApplicable = ltypeRank <= LONG_RANK && rtypeRank <= LONG_RANK
586                        || isBooleanType(ltype) && isBooleanType(rtype);
587         resultTypeRank = ltypeRank <= LONG_RANK ? INT_RANK : BOOL_RANK;
588       }
589     }
590     else if (tokenType == JavaTokenType.ANDAND || tokenType == JavaTokenType.OROR) {
591       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
592         isApplicable = isBooleanType(ltype) && isBooleanType(rtype);
593       }
594     }
595     if (isApplicable && strict) {
596       if (resultTypeRank > MAX_NUMERIC_RANK) {
597         isApplicable = ltypeRank == resultTypeRank || ltype.equalsToText(CommonClassNames.JAVA_LANG_OBJECT);
598       }
599       else {
600         isApplicable = ltypeRank <= MAX_NUMERIC_RANK;
601       }
602     }
603     return isApplicable;
604   }
605
606   public static boolean isPrimitiveAndNotNullOrWrapper(PsiType type) {
607     if (type instanceof PsiCapturedWildcardType) {
608       return isPrimitiveAndNotNullOrWrapper(((PsiCapturedWildcardType)type).getUpperBound());
609     }
610     if (type instanceof PsiClassType) {
611       return PsiPrimitiveType.getUnboxedType(type) != null;
612     }
613
614     return isPrimitiveAndNotNull(type);
615   }
616
617   public static boolean isUnaryOperatorApplicable(@NotNull PsiJavaToken token, PsiExpression operand) {
618     if (operand == null) return false;
619     PsiType type = operand.getType();
620     return type != null && isUnaryOperatorApplicable(token, type);
621   }
622
623   public static boolean isUnaryOperatorApplicable(@NotNull PsiJavaToken token, @NotNull PsiType type) {
624     IElementType i = token.getTokenType();
625     int typeRank = getTypeRank(type);
626     if (i == JavaTokenType.MINUSMINUS || i == JavaTokenType.PLUSPLUS) {
627       return typeRank <= MAX_NUMERIC_RANK;
628     }
629     if (i == JavaTokenType.MINUS || i == JavaTokenType.PLUS) {
630       return typeRank <= MAX_NUMERIC_RANK;
631     }
632     if (i == JavaTokenType.TILDE) {
633       return typeRank <= LONG_RANK;
634     }
635     if (i == JavaTokenType.EXCL) {
636       return typeRank == BOOL_RANK;
637     }
638     LOG.error("unknown token: " + token);
639     return true;
640   }
641
642   /**
643    * @return true if expression can be the left part of assignment operator
644    */
645   public static boolean isLValue(PsiExpression element) {
646     if (element instanceof PsiReferenceExpression) {
647       final PsiReferenceExpression expression = (PsiReferenceExpression)element;
648       final PsiElement resolved = expression.resolve();
649       return resolved instanceof PsiVariable;
650     }
651     if (element instanceof PsiParenthesizedExpression) {
652       return isLValue(((PsiParenthesizedExpression)element).getExpression());
653     }
654     if (element instanceof PsiArrayAccessExpression) {
655       final PsiArrayAccessExpression arrayAccessExpression = (PsiArrayAccessExpression)element;
656       final PsiExpression arrayExpression = arrayAccessExpression.getArrayExpression();
657       final PsiType type = arrayExpression.getType();
658       if (type == null || !(type instanceof PsiArrayType)) return false;
659       final PsiExpression indexExpression = arrayAccessExpression.getIndexExpression();
660       if (indexExpression == null) return false;
661       final PsiType indexType = indexExpression.getType();
662       if (indexType == null) return false;
663       if (getTypeRank(indexType) <= INT_RANK) return true;
664     }
665     return false;
666   }
667
668
669   /**
670    * JLS 5.2
671    */
672   public static boolean areTypesAssignmentCompatible(PsiType lType, PsiExpression rExpr) {
673     if (lType == null || rExpr == null) return true;
674     PsiType rType = rExpr.getType();
675     if (rType == null) return false;
676     if (isAssignable(lType, rType)) return true;
677     if (lType instanceof PsiClassType) {
678         lType = PsiPrimitiveType.getUnboxedType(lType);
679         if (lType == null) return false;
680     }
681
682     final int rTypeRank = getTypeRank(rType);
683     if (lType instanceof PsiPrimitiveType
684         && rType instanceof PsiPrimitiveType
685         && rTypeRank >= BYTE_RANK && rTypeRank <= INT_RANK) {
686       final Object rValue = JavaPsiFacade.getInstance(rExpr.getProject()).getConstantEvaluationHelper().computeConstantExpression(rExpr);
687       final long value;
688       if (rValue instanceof Number) {
689         value = ((Number)rValue).longValue();
690       }
691       else if (rValue instanceof Character) {
692         value = (Character)rValue;
693       }
694       else {
695         return false;
696       }
697
698       if (PsiType.BYTE.equals(lType)) {
699         return -128 <= value && value <= 127;
700       }
701       else if (PsiType.SHORT.equals(lType)) {
702         return -32768 <= value && value <= 32767;
703       }
704       else if (PsiType.CHAR.equals(lType)) {
705         return 0 <= value && value <= 0xFFFF;
706       }
707     }
708     return false;
709   }
710
711   /**
712    * Checks whether values of one type can be assigned to another
713    *
714    * @param left  type to assign to
715    * @param right type of value
716    * @return true if value of type <code>right</code> can be assigned to an l-value of
717    *         type <code>left</code>
718    */
719   public static boolean isAssignable(@NotNull PsiType left, @NotNull PsiType right) {
720     return isAssignable(left, right, true);
721   }
722
723   public static boolean isAssignable(@NotNull PsiType left, @NotNull PsiType right, boolean allowUncheckedConversion) {
724     return isAssignable(left, right, allowUncheckedConversion, true);
725   }
726
727   private static boolean isAssignable(@NotNull PsiType left,
728                                       @NotNull PsiType right,
729                                       boolean allowUncheckedConversion,
730                                       boolean capture) {
731     if (left == right || left.equals(right)) return true;
732
733     if (isNullType(right)) {
734       return !(left instanceof PsiPrimitiveType) || isNullType(left);
735     }
736
737     if (right instanceof PsiMethodReferenceType) {
738       final PsiMethodReferenceExpression methodReferenceExpression = ((PsiMethodReferenceType)right).getExpression();
739       if (left instanceof PsiLambdaExpressionType) {
740         final PsiType rType = methodReferenceExpression.getFunctionalInterfaceType();
741         final PsiType lType = ((PsiLambdaExpressionType)left).getExpression().getFunctionalInterfaceType();
742         return Comparing.equal(rType, lType);
743       } else if (left instanceof PsiMethodReferenceType) {
744         final PsiType rType = methodReferenceExpression.getFunctionalInterfaceType();
745         final PsiType lType = ((PsiMethodReferenceType)left).getExpression().getFunctionalInterfaceType();
746         return Comparing.equal(rType, lType);
747       }
748       return !(left instanceof PsiArrayType) && methodReferenceExpression.isAcceptable(left);
749     }
750     if (right instanceof PsiLambdaExpressionType) {
751       final PsiLambdaExpression rLambdaExpression = ((PsiLambdaExpressionType)right).getExpression();
752       if (left instanceof PsiLambdaExpressionType) {
753         final PsiLambdaExpression lLambdaExpression = ((PsiLambdaExpressionType)left).getExpression();
754         final PsiType rType = rLambdaExpression.getFunctionalInterfaceType();
755         final PsiType lType = lLambdaExpression.getFunctionalInterfaceType();
756         return Comparing.equal(rType, lType);
757       }
758       return !(left instanceof PsiArrayType) && rLambdaExpression.isAcceptable(left);
759     }
760
761     if (left instanceof PsiIntersectionType) {
762       PsiType[] conjuncts = ((PsiIntersectionType)left).getConjuncts();
763       for (PsiType conjunct : conjuncts) {
764         if (!isAssignable(conjunct, right, allowUncheckedConversion, capture)) return false;
765       }
766       return true;
767     }
768     if (right instanceof PsiIntersectionType) {
769       PsiType[] conjuncts = ((PsiIntersectionType)right).getConjuncts();
770       for (PsiType conjunct : conjuncts) {
771         if (isAssignable(left, conjunct, allowUncheckedConversion, capture)) return true;
772       }
773       return false;
774     }
775
776     if (right instanceof PsiCapturedWildcardType) {
777       return isAssignable(left, ((PsiCapturedWildcardType)right).getUpperBound(), allowUncheckedConversion, capture);
778     }
779
780     if (left instanceof PsiCapturedWildcardType) {
781       return left.equals(right) || isAssignable(((PsiCapturedWildcardType)left).getLowerBound(), right, allowUncheckedConversion, capture);
782     }
783
784     if (left instanceof PsiWildcardType) {
785       return isAssignableToWildcard((PsiWildcardType)left, right);
786     }
787     if (right instanceof PsiWildcardType) {
788       return isAssignableFromWildcard(left, (PsiWildcardType)right);
789     }
790     if (right instanceof PsiArrayType) {
791       if (!(left instanceof PsiArrayType)) {
792         if (left instanceof PsiPrimitiveType || PsiUtil.resolveClassInType(left) == null) return false;
793         PsiClass lClass = PsiUtil.resolveClassInType(left);
794         if (lClass == null) return false;
795         if (lClass.isInterface()) {
796           final String qualifiedName = lClass.getQualifiedName();
797           return "java.io.Serializable".equals(qualifiedName) || "java.lang.Cloneable".equals(qualifiedName);
798         }
799         else {
800           return left.equalsToText(CommonClassNames.JAVA_LANG_OBJECT);
801         }
802       }
803       PsiType lCompType = ((PsiArrayType)left).getComponentType();
804       PsiType rCompType = ((PsiArrayType)right).getComponentType();
805       if (lCompType instanceof PsiPrimitiveType) {
806         return lCompType.equals(rCompType);
807       }
808       return !(rCompType instanceof PsiPrimitiveType) && isAssignable(lCompType, rCompType, allowUncheckedConversion, capture);
809     }
810
811     if (left instanceof PsiDisjunctionType) {
812       for (PsiType type : ((PsiDisjunctionType)left).getDisjunctions()) {
813         if (isAssignable(type, right, allowUncheckedConversion, capture)) return true;
814       }
815       return false;
816     }
817     if (right instanceof PsiDisjunctionType) {
818       return isAssignable(left, ((PsiDisjunctionType)right).getLeastUpperBound(), allowUncheckedConversion, capture);
819     }
820
821     if (left instanceof PsiArrayType) return false;
822     if (right instanceof PsiPrimitiveType) {
823       if (isVoidType(right)) return false;
824       if (!(left instanceof PsiPrimitiveType)) {
825         return left instanceof PsiClassType && isBoxable((PsiClassType)left, (PsiPrimitiveType)right);
826       }
827       int leftTypeIndex = TYPE_TO_RANK_MAP.get(left) - 1;
828       int rightTypeIndex = TYPE_TO_RANK_MAP.get(right) - 1;
829       return leftTypeIndex >= 0 &&
830              rightTypeIndex >= 0 &&
831              rightTypeIndex < IS_ASSIGNABLE_BIT_SET.length &&
832              leftTypeIndex < IS_ASSIGNABLE_BIT_SET.length &&
833              IS_ASSIGNABLE_BIT_SET[rightTypeIndex][leftTypeIndex];
834     }
835     if (!(right instanceof PsiClassType)) {
836       return false; // must be TypeCook's PsiTypeVariable
837     }
838     if (left instanceof PsiPrimitiveType) {
839       return isUnboxable((PsiPrimitiveType)left, (PsiClassType)right, new HashSet<PsiClassType>());
840     }
841     final PsiClassType.ClassResolveResult leftResult = PsiUtil.resolveGenericsClassInType(left);
842     final PsiClassType.ClassResolveResult rightResult = PsiUtil.resolveGenericsClassInType(right);
843     if (leftResult.getElement() == null || rightResult.getElement() == null) {
844       if (leftResult.getElement() != rightResult.getElement()) return false;
845       // let's suppose 2 unknown classes, which could be the same to be the same
846       String lText = left.getPresentableText();
847       String rText = right.getPresentableText();
848       if (lText.equals(rText)) return true;
849       if (lText.length() > rText.length() && lText.endsWith(rText) &&
850           lText.charAt(lText.length() - rText.length() - 1) == '.') {
851         return true;
852       }
853       return rText.length() > lText.length()
854              && rText.endsWith(lText)
855              && rText.charAt(rText.length() - lText.length() - 1) == '.';
856     }
857     return isClassAssignable(leftResult, rightResult, allowUncheckedConversion, left.getResolveScope(), capture);
858   }
859
860   private static boolean isAssignableFromWildcard(@NotNull PsiType left, @NotNull PsiWildcardType rightWildcardType) {
861     if (rightWildcardType.isSuper()) {
862       final PsiClass aClass = PsiUtil.resolveClassInType(rightWildcardType.getSuperBound());
863       if (aClass instanceof PsiTypeParameter) {
864         final PsiClassType[] types = aClass.getExtendsListTypes();
865         for (PsiClassType type : types) {
866           if (isAssignable(left, type)) return true;
867         }
868       }
869     }
870     return isAssignable(left, rightWildcardType.getExtendsBound());
871   }
872
873   private static boolean isAssignableToWildcard(@NotNull PsiWildcardType wildcardType, @NotNull PsiType right) {
874     if (wildcardType.isSuper()) {
875       return isAssignable(wildcardType.getSuperBound(), right);
876     }
877     return isAssignable(wildcardType.getExtendsBound(), right);
878   }
879
880   private static boolean isUnboxable(@NotNull PsiPrimitiveType left, @NotNull PsiClassType right, @NotNull Set<PsiClassType> types) {
881     if (!right.getLanguageLevel().isAtLeast(LanguageLevel.JDK_1_5)) return false;
882     final PsiClass psiClass = right.resolve();
883     if (psiClass == null) return false;
884
885     if (psiClass instanceof PsiTypeParameter) {
886       for (PsiClassType bound : psiClass.getExtendsListTypes()) {
887         if (types.add(bound) && isUnboxable(left, bound, types)) {
888           return true;
889         }
890       }
891       return false;
892     }
893
894     final PsiPrimitiveType rightUnboxedType = PsiPrimitiveType.getUnboxedType(right);
895     return rightUnboxedType != null && isAssignable(left, rightUnboxedType);
896   }
897
898   public static boolean boxingConversionApplicable(final PsiType left, final PsiType right) {
899     if (left instanceof PsiPrimitiveType && !PsiType.NULL.equals(left)) {
900       return right instanceof PsiClassType && isAssignable(left, right);
901     }
902
903     if (left instanceof PsiIntersectionType) {
904       for (PsiType lConjunct : ((PsiIntersectionType)left).getConjuncts()) {
905         if (!boxingConversionApplicable(lConjunct, right)) return false;
906       }
907       return true;
908     }
909
910     return left instanceof PsiClassType
911               && right instanceof PsiPrimitiveType
912               && !PsiType.NULL.equals(right)
913               && isAssignable(left, right);
914   }
915
916   private static final Key<CachedValue<Set<String>>> POSSIBLE_BOXED_HOLDER_TYPES = Key.create("Types that may be possibly assigned from primitive ones");
917
918   private static boolean isBoxable(@NotNull PsiClassType left, @NotNull PsiPrimitiveType right) {
919     if (!left.getLanguageLevel().isAtLeast(LanguageLevel.JDK_1_5)) return false;
920     final PsiClass psiClass = left.resolve();
921     if (psiClass == null) return false;
922
923     final String qname = psiClass.getQualifiedName();
924     if (qname == null || !(psiClass instanceof PsiTypeParameter || getAllBoxedTypeSupers(psiClass).contains(qname))) {
925       return false;
926     }
927
928     final PsiClassType rightBoxed = right.getBoxedType(psiClass.getManager(), left.getResolveScope());
929     return rightBoxed != null && isAssignable(left, rightBoxed);
930   }
931
932   @NotNull
933   private static Set<String> getAllBoxedTypeSupers(@NotNull PsiClass psiClass) {
934     PsiManager manager = psiClass.getManager();
935     final Project project = psiClass.getProject();
936     CachedValue<Set<String>> boxedHolderTypes = project.getUserData(POSSIBLE_BOXED_HOLDER_TYPES);
937     if (boxedHolderTypes == null) {
938       project.putUserData(POSSIBLE_BOXED_HOLDER_TYPES, boxedHolderTypes = CachedValuesManager.getManager(manager.getProject()).createCachedValue(new CachedValueProvider<Set<String>>() {
939         @Override
940         public Result<Set<String>> compute() {
941           final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
942           final Set<String> set = new THashSet<String>();
943           for (final String qname : PsiPrimitiveType.getAllBoxedTypeNames()) {
944             final PsiClass boxedClass = facade.findClass(qname, GlobalSearchScope.allScope(project));
945             InheritanceUtil.processSupers(boxedClass, true, new Processor<PsiClass>() {
946               @Override
947               public boolean process(PsiClass psiClass) {
948                 ContainerUtil.addIfNotNull(psiClass.getQualifiedName(), set);
949                 return true;
950               }
951             });
952           }
953           return Result.create(set, ProjectRootModificationTracker.getInstance(project));
954         }
955       }, false));
956     }
957
958     return boxedHolderTypes.getValue();
959   }
960
961   private static boolean isClassAssignable(@NotNull PsiClassType.ClassResolveResult leftResult,
962                                            @NotNull PsiClassType.ClassResolveResult rightResult,
963                                            boolean allowUncheckedConversion, 
964                                            GlobalSearchScope resolveScope, 
965                                            boolean capture) {
966     final PsiClass leftClass = leftResult.getElement();
967     final PsiClass rightClass = rightResult.getElement();
968     if (leftClass == null || rightClass == null) return false;
969
970     PsiSubstitutor superSubstitutor = JavaClassSupers.getInstance().getSuperClassSubstitutor(leftClass, rightClass, resolveScope,
971                                                                                              rightResult.getSubstitutor());
972     return superSubstitutor != null && typeParametersAgree(leftResult, rightResult, allowUncheckedConversion, superSubstitutor, capture);
973   }
974
975   private static boolean typeParametersAgree(@NotNull PsiClassType.ClassResolveResult leftResult,
976                                              @NotNull PsiClassType.ClassResolveResult rightResult,
977                                              boolean allowUncheckedConversion, PsiSubstitutor superSubstitutor,
978                                              boolean capture) {
979     PsiSubstitutor rightSubstitutor = rightResult.getSubstitutor();
980     PsiClass leftClass = leftResult.getElement();
981     PsiClass rightClass = rightResult.getElement();
982
983     Iterator<PsiTypeParameter> li = PsiUtil.typeParametersIterator(leftClass);
984
985     if (!li.hasNext()) return true;
986     PsiSubstitutor leftSubstitutor = leftResult.getSubstitutor();
987
988     if (!leftClass.getManager().areElementsEquivalent(leftClass, rightClass)) {
989       rightSubstitutor = superSubstitutor;
990       rightClass = leftClass;
991     }
992     else if (!PsiUtil.typeParametersIterator(rightClass).hasNext()) return true;
993
994     Iterator<PsiTypeParameter> ri = PsiUtil.typeParametersIterator(rightClass);
995     while (li.hasNext()) {
996       if (!ri.hasNext()) return false;
997       PsiTypeParameter lp = li.next();
998       PsiTypeParameter rp = ri.next();
999       final PsiType typeLeft = leftSubstitutor.substitute(lp);
1000       if (typeLeft == null) continue;
1001       final PsiType typeRight = PsiCapturedWildcardType.isCapture() && capture
1002                                 ? rightSubstitutor.substituteWithBoundsPromotion(rp)
1003                                 : rightSubstitutor.substitute(rp);
1004       if (typeRight == null) {
1005         // compatibility feature: allow to assign raw types to generic ones
1006         return allowUncheckedConversion;
1007       }
1008       if (!typesAgree(typeLeft, typeRight, allowUncheckedConversion)) {
1009         return false;
1010       }
1011     }
1012     return true;
1013   }
1014
1015   private static final RecursionGuard ourGuard = RecursionManager.createGuard("isAssignable");
1016
1017   public static boolean typesAgree(final @NotNull PsiType typeLeft,
1018                                    final @NotNull PsiType typeRight,
1019                                    final boolean allowUncheckedConversion) {
1020     if (typeLeft instanceof PsiWildcardType) {
1021       final PsiWildcardType leftWildcard = (PsiWildcardType)typeLeft;
1022       final PsiType leftBound = leftWildcard.getBound();
1023       if (leftBound == null) return true;
1024       if (leftBound.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
1025         if (!leftWildcard.isSuper()) return true;
1026         if (typeRight.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) return true;
1027       }
1028
1029       if (typeRight instanceof PsiWildcardType) {
1030         final PsiWildcardType rightWildcard = (PsiWildcardType)typeRight;
1031         if (leftWildcard.isExtends()) {
1032           return rightWildcard.isExtends() && isAssignable(leftBound, rightWildcard.getBound(), allowUncheckedConversion, false);
1033         }
1034         else { //isSuper
1035           if (rightWildcard.isSuper()) {
1036             final Boolean assignable = ourGuard.doPreventingRecursion(rightWildcard, true, new NotNullComputable<Boolean>() {
1037               @NotNull
1038               @Override
1039               public Boolean compute() {
1040                 return isAssignable(rightWildcard.getBound(), leftBound, allowUncheckedConversion, false);
1041               }
1042             });
1043             if (assignable != null && assignable) {
1044               return true;
1045             }
1046           }
1047           return false;
1048         }
1049       }
1050       else {
1051         if (leftWildcard.isExtends()) {
1052           return isAssignable(leftBound, typeRight, false, false);
1053         }
1054         else { // isSuper
1055           final Boolean assignable = ourGuard.doPreventingRecursion(leftWildcard, true, new NotNullComputable<Boolean>() {
1056             @NotNull
1057             @Override
1058             public Boolean compute() {
1059               return isAssignable(typeRight, leftBound, false, false);
1060             }
1061           });
1062           return assignable == null || assignable.booleanValue(); 
1063         }
1064       }
1065     }
1066     else {
1067       return typeLeft.equals(typeRight);
1068     }
1069   }
1070
1071   @Nullable
1072   public static PsiSubstitutor getClassSubstitutor(@NotNull PsiClass superClassCandidate,
1073                                                    @NotNull PsiClass derivedClassCandidate,
1074                                                    @NotNull PsiSubstitutor derivedSubstitutor) {
1075     if (superClassCandidate.getManager().areElementsEquivalent(superClassCandidate, derivedClassCandidate)) {
1076       PsiTypeParameter[] baseParams = superClassCandidate.getTypeParameters();
1077       PsiTypeParameter[] derivedParams = derivedClassCandidate.getTypeParameters();
1078       if (baseParams.length > 0 && derivedParams.length == 0) {
1079         return JavaPsiFacade.getInstance(superClassCandidate.getProject()).getElementFactory().createRawSubstitutor(superClassCandidate);
1080       }
1081       return derivedSubstitutor;
1082     }
1083     return getMaybeSuperClassSubstitutor(superClassCandidate, derivedClassCandidate, derivedSubstitutor, null);
1084   }
1085
1086   private static final Set<String> ourReportedSuperClassSubstitutorExceptions = ContainerUtil.newConcurrentSet();
1087
1088   /**
1089    * Calculates substitutor that binds type parameters in <code>superClass</code> with
1090    * values that they have in <code>derivedClass</code>, given that type parameters in
1091    * <code>derivedClass</code> are bound by <code>derivedSubstitutor</code>.
1092    * <code>superClass</code> must be a super class/interface of <code>derivedClass</code> (as in
1093    * <code>InheritanceUtil.isInheritorOrSelf(derivedClass, superClass, true)</code>
1094    *
1095    * @return substitutor (never returns <code>null</code>)
1096    * @see PsiClass#isInheritor(PsiClass, boolean)
1097    * @see InheritanceUtil#isInheritorOrSelf(PsiClass, PsiClass, boolean)
1098    */
1099   @NotNull
1100   public static PsiSubstitutor getSuperClassSubstitutor(@NotNull PsiClass superClass,
1101                                                         @NotNull PsiClass derivedClass,
1102                                                         @NotNull PsiSubstitutor derivedSubstitutor) {
1103     if (!superClass.hasTypeParameters() && superClass.getContainingClass() == null) return PsiSubstitutor.EMPTY; //optimization and protection against EJB queer hierarchy
1104
1105     Set<PsiClass> visited = new THashSet<PsiClass>();
1106     PsiSubstitutor substitutor = getMaybeSuperClassSubstitutor(superClass, derivedClass, derivedSubstitutor, visited);
1107
1108     if (substitutor == null) {
1109       if (ourReportedSuperClassSubstitutorExceptions.add(derivedClass.getQualifiedName() + "/" + superClass.getQualifiedName())) {
1110         reportHierarchyInconsistency(superClass, derivedClass, visited);
1111       }
1112       return PsiSubstitutor.EMPTY;
1113     }
1114     return substitutor;
1115   }
1116
1117   // the same as getSuperClassSubstitutor() but can return null, which means that classes were not inheritors
1118   @Nullable
1119   public static PsiSubstitutor getMaybeSuperClassSubstitutor(@NotNull PsiClass superClass,
1120                                                              @NotNull PsiClass derivedClass,
1121                                                              @NotNull PsiSubstitutor derivedSubstitutor,
1122                                                              @Nullable Set<PsiClass> visited) {
1123     return JavaClassSupers.getInstance().getSuperClassSubstitutor(superClass, derivedClass, derivedClass.getResolveScope(), derivedSubstitutor);
1124   }
1125
1126   private static void reportHierarchyInconsistency(@NotNull PsiClass superClass, @NotNull PsiClass derivedClass, @NotNull Set<PsiClass> visited) {
1127     final StringBuilder msg = new StringBuilder("Super: " + classInfo(superClass));
1128     msg.append("visited:\n");
1129     for (PsiClass aClass : visited) {
1130       msg.append("  each: " + classInfo(aClass));
1131     }
1132     msg.append("isInheritor: " + InheritanceUtil.isInheritorOrSelf(derivedClass, superClass, true) + " " + derivedClass.isInheritor(superClass, true));
1133     msg.append("\nhierarchy:\n");
1134     InheritanceUtil.processSupers(derivedClass, true, new Processor<PsiClass>() {
1135       @Override
1136       public boolean process(PsiClass psiClass) {
1137         msg.append("each: " + classInfo(psiClass));
1138         return true;
1139       }
1140     });
1141     LOG.error(msg.toString());
1142   }
1143
1144   @NotNull
1145   private static String classInfo(@NotNull PsiClass aClass) {
1146     String s = aClass.getQualifiedName() + "(" + aClass.getClass().getName() + "; " + PsiUtilCore.getVirtualFile(aClass) + ");\n";
1147     s += "extends: ";
1148     for (PsiClassType type : aClass.getExtendsListTypes()) {
1149       s += type + " (" + type.getClass().getName() + "; " + type.resolve() + ") ";
1150     }
1151     s += "\nimplements: ";
1152     for (PsiClassType type : aClass.getImplementsListTypes()) {
1153       s += type + " (" + type.getClass().getName() + "; " + type.resolve() + ") ";
1154     }
1155     return s + "\n";
1156   }
1157
1158   @NotNull
1159   public static PsiSubstitutor getSuperClassSubstitutor(@NotNull PsiClass superClass, @NotNull PsiClassType classType) {
1160       final PsiClassType.ClassResolveResult classResolveResult = classType.resolveGenerics();
1161       return getSuperClassSubstitutor(superClass, classResolveResult.getElement(), classResolveResult.getSubstitutor());
1162   }
1163
1164   /**
1165    * see JLS 5.6.2
1166    */
1167   @NotNull
1168   public static PsiType binaryNumericPromotion(PsiType type1, PsiType type2) {
1169     if (isDoubleType(type1)) return unbox(type1);
1170     if (isDoubleType(type2)) return unbox(type2);
1171     if (isFloatType(type1)) return unbox(type1);
1172     if (isFloatType(type2)) return unbox(type2);
1173     if (isLongType(type1)) return unbox(type1);
1174     if (isLongType(type2)) return unbox(type2);
1175
1176     return PsiType.INT;
1177   }
1178
1179   @NotNull
1180   private static PsiType unbox(@NotNull PsiType type) {
1181     if (type instanceof PsiPrimitiveType) return type;
1182     if (type instanceof PsiClassType) {
1183       type = PsiPrimitiveType.getUnboxedType(type);
1184       LOG.assertTrue(type != null);
1185       return type;
1186     }
1187     LOG.error("Invalid type for unboxing "+type);
1188     return type;
1189   }
1190
1191   private static final Set<String> INTEGER_NUMBER_TYPES = new THashSet<String>(5);
1192
1193   static {
1194     INTEGER_NUMBER_TYPES.add(PsiType.BYTE.getCanonicalText());
1195     INTEGER_NUMBER_TYPES.add(PsiType.CHAR.getCanonicalText());
1196     INTEGER_NUMBER_TYPES.add(PsiType.LONG.getCanonicalText());
1197     INTEGER_NUMBER_TYPES.add(PsiType.INT.getCanonicalText());
1198     INTEGER_NUMBER_TYPES.add(PsiType.SHORT.getCanonicalText());
1199   }
1200
1201   private static final Set<String> PRIMITIVE_TYPES = new THashSet<String>(9);
1202
1203   static {
1204     PRIMITIVE_TYPES.add(PsiType.VOID.getCanonicalText());
1205     PRIMITIVE_TYPES.add(PsiType.BYTE.getCanonicalText());
1206     PRIMITIVE_TYPES.add(PsiType.CHAR.getCanonicalText());
1207     PRIMITIVE_TYPES.add(PsiType.DOUBLE.getCanonicalText());
1208     PRIMITIVE_TYPES.add(PsiType.FLOAT.getCanonicalText());
1209     PRIMITIVE_TYPES.add(PsiType.LONG.getCanonicalText());
1210     PRIMITIVE_TYPES.add(PsiType.INT.getCanonicalText());
1211     PRIMITIVE_TYPES.add(PsiType.SHORT.getCanonicalText());
1212     PRIMITIVE_TYPES.add(PsiType.BOOLEAN.getCanonicalText());
1213   }
1214
1215   private static final Set<String> PRIMITIVE_WRAPPER_TYPES = new THashSet<String>(8);
1216
1217   static {
1218     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Byte");
1219     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Character");
1220     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Double");
1221     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Float");
1222     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Long");
1223     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Integer");
1224     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Short");
1225     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Boolean");
1226   }
1227
1228   public static boolean isIntegerNumber(String typeName) {
1229     return INTEGER_NUMBER_TYPES.contains(typeName);
1230   }
1231
1232   public static boolean isPrimitive(String typeName) {
1233     return PRIMITIVE_TYPES.contains(typeName);
1234   }
1235
1236   public static boolean isPrimitiveWrapper(String typeName) {
1237     return PRIMITIVE_WRAPPER_TYPES.contains(typeName);
1238   }
1239   @Contract("null -> false")
1240   public static boolean isAssignableFromPrimitiveWrapper(final PsiType type) {
1241     if (type == null) return false;
1242     return isPrimitiveWrapper(type) ||
1243            type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) ||
1244            type.equalsToText(CommonClassNames.JAVA_LANG_NUMBER);
1245   }
1246
1247   @Contract("null -> false")
1248   public static boolean isPrimitiveWrapper(final PsiType type) {
1249     return type != null && isPrimitiveWrapper(type.getCanonicalText());
1250   }
1251
1252   @Contract("null -> false")
1253   public static boolean isComposite(final PsiType type) {
1254     return type instanceof PsiDisjunctionType || type instanceof PsiIntersectionType;
1255   }
1256
1257   public static PsiType typeParameterErasure(@NotNull PsiTypeParameter typeParameter) {
1258     return typeParameterErasure(typeParameter, PsiSubstitutor.EMPTY);
1259   }
1260
1261   private static PsiType typeParameterErasure(@NotNull PsiTypeParameter typeParameter, @NotNull PsiSubstitutor beforeSubstitutor) {
1262     final PsiClassType[] extendsList = typeParameter.getExtendsList().getReferencedTypes();
1263     if (extendsList.length > 0) {
1264       final PsiClass psiClass = extendsList[0].resolve();
1265       if (psiClass instanceof PsiTypeParameter) {
1266         Set<PsiClass> visited = new THashSet<PsiClass>();
1267         visited.add(psiClass);
1268         final PsiTypeParameter boundTypeParameter = (PsiTypeParameter)psiClass;
1269         if (beforeSubstitutor.getSubstitutionMap().containsKey(boundTypeParameter)) {
1270           return erasure(beforeSubstitutor.substitute(boundTypeParameter));
1271         }
1272         return typeParameterErasureInner(boundTypeParameter, visited, beforeSubstitutor);
1273       }
1274       else if (psiClass != null) {
1275         return JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(psiClass);
1276       }
1277     }
1278     return PsiType.getJavaLangObject(typeParameter.getManager(), typeParameter.getResolveScope());
1279   }
1280
1281   private static PsiClassType typeParameterErasureInner(PsiTypeParameter typeParameter,
1282                                                         Set<PsiClass> visited,
1283                                                         PsiSubstitutor beforeSubstitutor) {
1284     final PsiClassType[] extendsList = typeParameter.getExtendsList().getReferencedTypes();
1285     if (extendsList.length > 0) {
1286       final PsiClass psiClass = extendsList[0].resolve();
1287       if (psiClass instanceof PsiTypeParameter) {
1288         if (!visited.contains(psiClass)) {
1289           visited.add(psiClass);
1290           if (beforeSubstitutor.getSubstitutionMap().containsKey(psiClass)) {
1291             return (PsiClassType)erasure(beforeSubstitutor.substitute((PsiTypeParameter)psiClass));
1292           }
1293           return typeParameterErasureInner((PsiTypeParameter)psiClass, visited, beforeSubstitutor);
1294         }
1295       }
1296       else if (psiClass != null) {
1297         return JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(psiClass);
1298       }
1299     }
1300     return PsiType.getJavaLangObject(typeParameter.getManager(), typeParameter.getResolveScope());
1301   }
1302
1303   @Contract("null -> null")
1304   public static PsiType erasure(@Nullable PsiType type) {
1305     return erasure(type, PsiSubstitutor.EMPTY);
1306   }
1307
1308   @Contract("null, _ -> null")
1309   public static PsiType erasure(@Nullable final PsiType type, @NotNull final PsiSubstitutor beforeSubstitutor) {
1310     if (type == null) return null;
1311     return type.accept(new PsiTypeVisitor<PsiType>() {
1312       @Nullable
1313       @Override
1314       public PsiType visitType(PsiType type) {
1315         return type;
1316       }
1317
1318       @Override
1319       public PsiType visitClassType(PsiClassType classType) {
1320         final PsiClass aClass = classType.resolve();
1321         if (aClass instanceof PsiTypeParameter && !isFreshVariable((PsiTypeParameter)aClass)) {
1322           return typeParameterErasure((PsiTypeParameter)aClass, beforeSubstitutor);
1323         }
1324         return classType.rawType();
1325       }
1326
1327       @Override
1328       public PsiType visitWildcardType(PsiWildcardType wildcardType) {
1329         return wildcardType;
1330       }
1331
1332       @Nullable
1333       @Override
1334       public PsiType visitCapturedWildcardType(PsiCapturedWildcardType capturedWildcardType) {
1335         return capturedWildcardType.getUpperBound().accept(this);
1336       }
1337
1338       @Override
1339       public PsiType visitPrimitiveType(PsiPrimitiveType primitiveType) {
1340         return primitiveType;
1341       }
1342
1343       @Override
1344       public PsiType visitEllipsisType(PsiEllipsisType ellipsisType) {
1345         final PsiType componentType = ellipsisType.getComponentType();
1346         final PsiType newComponentType = componentType.accept(this);
1347         if (newComponentType == componentType) return ellipsisType;
1348         return newComponentType != null ? newComponentType.createArrayType() : null;
1349       }
1350
1351       @Override
1352       public PsiType visitArrayType(PsiArrayType arrayType) {
1353         final PsiType componentType = arrayType.getComponentType();
1354         final PsiType newComponentType = componentType.accept(this);
1355         if (newComponentType == componentType) return arrayType;
1356         return newComponentType != null ? newComponentType.createArrayType() : null;
1357       }
1358
1359       @Override
1360       public PsiType visitDisjunctionType(PsiDisjunctionType disjunctionType) {
1361         final PsiClassType lub = PsiTypesUtil.getLowestUpperBoundClassType(disjunctionType);
1362         return lub != null ? erasure(lub, beforeSubstitutor) : disjunctionType;
1363       }
1364     });
1365   }
1366
1367   public static Object computeCastTo(final Object operand, final PsiType castType) {
1368     if (operand == null || castType == null) return null;
1369     Object value;
1370     if (operand instanceof String && castType.equalsToText(JAVA_LANG_STRING)) {
1371       value = operand;
1372     }
1373     else if (operand instanceof Boolean && PsiType.BOOLEAN.equals(castType)) {
1374       value = operand;
1375     }
1376     else {
1377       final PsiType primitiveType = wrapperToPrimitive(operand);
1378       if (primitiveType == null) return null;
1379       // identity cast, including (boolean)boolValue
1380       if (castType.equals(primitiveType)) return operand;
1381       final int rankFrom = getTypeRank(primitiveType);
1382       if (rankFrom > caster.length) return null;
1383       final int rankTo = getTypeRank(castType);
1384       if (rankTo > caster.length) return null;
1385
1386       value = caster[rankFrom - 1][rankTo - 1].cast(operand);
1387     }
1388     return value;
1389   }
1390
1391   @NotNull
1392   public static PsiType unboxAndBalanceTypes(PsiType type1, PsiType type2) {
1393     if (type1 instanceof PsiClassType) type1 = PsiPrimitiveType.getUnboxedType(type1);
1394     if (type2 instanceof PsiClassType) type2 = PsiPrimitiveType.getUnboxedType(type2);
1395
1396     if (PsiType.DOUBLE.equals(type1) || PsiType.DOUBLE.equals(type2)) return PsiType.DOUBLE;
1397     if (PsiType.FLOAT.equals(type1) || PsiType.FLOAT.equals(type2)) return PsiType.FLOAT;
1398     if (PsiType.LONG.equals(type1) || PsiType.LONG.equals(type2)) return PsiType.LONG;
1399     return PsiType.INT;
1400   }
1401
1402   public static IElementType convertEQtoOperation(IElementType eqOpSign) {
1403     IElementType opSign = null;
1404     if (eqOpSign == JavaTokenType.ANDEQ) {
1405       opSign = JavaTokenType.AND;
1406     }
1407     else if (eqOpSign == JavaTokenType.ASTERISKEQ) {
1408       opSign = JavaTokenType.ASTERISK;
1409     }
1410     else if (eqOpSign == JavaTokenType.DIVEQ) {
1411       opSign = JavaTokenType.DIV;
1412     }
1413     else if (eqOpSign == JavaTokenType.GTGTEQ) {
1414       opSign = JavaTokenType.GTGT;
1415     }
1416     else if (eqOpSign == JavaTokenType.GTGTGTEQ) {
1417       opSign = JavaTokenType.GTGTGT;
1418     }
1419     else if (eqOpSign == JavaTokenType.LTLTEQ) {
1420       opSign = JavaTokenType.LTLT;
1421     }
1422     else if (eqOpSign == JavaTokenType.MINUSEQ) {
1423       opSign = JavaTokenType.MINUS;
1424     }
1425     else if (eqOpSign == JavaTokenType.OREQ) {
1426       opSign = JavaTokenType.OR;
1427     }
1428     else if (eqOpSign == JavaTokenType.PERCEQ) {
1429       opSign = JavaTokenType.PERC;
1430     }
1431     else if (eqOpSign == JavaTokenType.PLUSEQ) {
1432       opSign = JavaTokenType.PLUS;
1433     }
1434     else if (eqOpSign == JavaTokenType.XOREQ) {
1435       opSign = JavaTokenType.XOR;
1436     }
1437     return opSign;
1438   }
1439
1440   @Nullable
1441   public static PsiType calcTypeForBinaryExpression(PsiType lType, PsiType rType, @NotNull IElementType sign, boolean accessLType) {
1442     if (sign == JavaTokenType.PLUS) {
1443       // evaluate right argument first, since '+-/*%' is left associative and left operand tends to be bigger
1444       if (rType == null) return null;
1445       if (rType.equalsToText(JAVA_LANG_STRING)) {
1446         return rType;
1447       }
1448       if (!accessLType) return NULL_TYPE;
1449       if (lType == null) return null;
1450       if (lType.equalsToText(JAVA_LANG_STRING)) {
1451         return lType;
1452       }
1453       return unboxAndBalanceTypes(lType, rType);
1454     }
1455     if (sign == JavaTokenType.MINUS || sign == JavaTokenType.ASTERISK || sign == JavaTokenType.DIV || sign == JavaTokenType.PERC) {
1456       if (rType == null) return null;
1457       if (!accessLType) return NULL_TYPE;
1458       if (lType == null) return null;
1459       return unboxAndBalanceTypes(lType, rType);
1460     }
1461     if (sign == JavaTokenType.LTLT || sign == JavaTokenType.GTGT || sign == JavaTokenType.GTGTGT) {
1462       if (!accessLType) return NULL_TYPE;
1463       if (PsiType.BYTE.equals(lType) || PsiType.CHAR.equals(lType) || PsiType.SHORT.equals(lType)) {
1464         return PsiType.INT;
1465       }
1466       if (lType instanceof PsiClassType) lType = PsiPrimitiveType.getUnboxedType(lType);
1467       return lType;
1468     }
1469     if (sign == JavaTokenType.EQEQ ||
1470         sign == JavaTokenType.NE ||
1471         sign == JavaTokenType.LT ||
1472         sign == JavaTokenType.GT ||
1473         sign == JavaTokenType.LE ||
1474         sign == JavaTokenType.GE ||
1475         sign == JavaTokenType.OROR ||
1476         sign == JavaTokenType.ANDAND) {
1477       return PsiType.BOOLEAN;
1478     }
1479     if (sign == JavaTokenType.OR || sign == JavaTokenType.XOR || sign == JavaTokenType.AND) {
1480       if (rType instanceof PsiClassType) rType = PsiPrimitiveType.getUnboxedType(rType);
1481
1482       if (lType instanceof PsiClassType) lType = PsiPrimitiveType.getUnboxedType(lType);
1483
1484       if (rType == null) return null;
1485       if (PsiType.BOOLEAN.equals(rType)) return PsiType.BOOLEAN;
1486       if (!accessLType) return NULL_TYPE;
1487       if (lType == null) return null;
1488       if (PsiType.BOOLEAN.equals(lType)) return PsiType.BOOLEAN;
1489       if (PsiType.LONG.equals(lType) || PsiType.LONG.equals(rType)) return PsiType.LONG;
1490       return PsiType.INT;
1491     }
1492     LOG.error("Unknown token: "+sign);
1493     return null;
1494   }
1495
1496   /**
1497    * See JLS 3.10.2. Floating-Point Literals
1498    * @return true  if floating point literal consists of zeros only
1499    */
1500   public static boolean isFPZero(@NotNull final String text) {
1501     for (int i = 0; i < text.length(); i++) {
1502       final char c = text.charAt(i);
1503       if (Character.isDigit(c) && c != '0') return false;
1504       final char d = Character.toUpperCase(c);
1505       if (d == 'E' || d == 'P') break;
1506     }
1507     return true;
1508   }
1509
1510   public static boolean areSameFreshVariables(PsiTypeParameter p1, PsiTypeParameter p2) {
1511     final PsiElement originalContext = p1.getUserData(ORIGINAL_CONTEXT);
1512     return originalContext != null && originalContext == p2.getUserData(ORIGINAL_CONTEXT);
1513   }
1514
1515   public static boolean isFreshVariable(PsiTypeParameter typeParameter) {
1516     return typeParameter.getUserData(ORIGINAL_CONTEXT) != null;
1517   }
1518   
1519   public static void markAsFreshVariable(PsiTypeParameter parameter, PsiElement context) {
1520     parameter.putUserData(ORIGINAL_CONTEXT, context);
1521   }
1522
1523   private interface Caster {
1524     @NotNull
1525     Object cast(@NotNull Object operand);
1526   }
1527
1528   private static final Caster[][] caster = {
1529     {
1530       new Caster() {
1531         @NotNull
1532         @Override
1533         public Object cast(@NotNull Object operand) {
1534           return operand;
1535         }
1536       }
1537       , new Caster() {
1538         @NotNull
1539         @Override
1540         public Object cast(@NotNull Object operand) {
1541           return (short)((Number)operand).intValue();
1542         }
1543       }
1544       , new Caster() {
1545         @NotNull
1546         @Override
1547         public Object cast(@NotNull Object operand) {
1548           return (char)((Number)operand).intValue();
1549         }
1550       }
1551       , new Caster() {
1552         @NotNull
1553         @Override
1554         public Object cast(@NotNull Object operand) {
1555           return ((Number)operand).intValue();
1556         }
1557       }
1558       , new Caster() {
1559         @NotNull
1560         @Override
1561         public Object cast(@NotNull Object operand) {
1562           return (long)((Number)operand).intValue();
1563         }
1564       }
1565       , new Caster() {
1566         @NotNull
1567         @Override
1568         public Object cast(@NotNull Object operand) {
1569           return (float)((Number)operand).intValue();
1570         }
1571       }
1572       , new Caster() {
1573         @NotNull
1574         @Override
1575         public Object cast(@NotNull Object operand) {
1576           return (double)((Number)operand).intValue();
1577         }
1578       }
1579     }
1580     ,
1581     {
1582       new Caster() {
1583         @NotNull
1584         @Override
1585         public Object cast(@NotNull Object operand) {
1586           return (byte)((Short)operand).shortValue();
1587         }
1588       }
1589       , new Caster() {
1590         @NotNull
1591         @Override
1592         public Object cast(@NotNull Object operand) {
1593           return operand;
1594         }
1595       }
1596       , new Caster() {
1597         @NotNull
1598         @Override
1599         public Object cast(@NotNull Object operand) {
1600           return (char)((Short)operand).shortValue();
1601         }
1602       }
1603       , new Caster() {
1604         @NotNull
1605         @Override
1606         public Object cast(@NotNull Object operand) {
1607           return (int)(Short)operand;
1608         }
1609       }
1610       , new Caster() {
1611         @NotNull
1612         @Override
1613         public Object cast(@NotNull Object operand) {
1614           return (long)(Short)operand;
1615         }
1616       }
1617       , new Caster() {
1618         @NotNull
1619         @Override
1620         public Object cast(@NotNull Object operand) {
1621           return (float)(Short)operand;
1622         }
1623       }
1624       , new Caster() {
1625         @NotNull
1626         @Override
1627         public Object cast(@NotNull Object operand) {
1628           return (double)(Short)operand;
1629         }
1630       }
1631     }
1632     ,
1633     {
1634       new Caster() {
1635         @NotNull
1636         @Override
1637         public Object cast(@NotNull Object operand) {
1638           return (byte)((Character)operand).charValue();
1639         }
1640       }
1641       , new Caster() {
1642         @NotNull
1643         @Override
1644         public Object cast(@NotNull Object operand) {
1645           return (short)((Character)operand).charValue();
1646         }
1647       }
1648       , new Caster() {
1649         @NotNull
1650         @Override
1651         public Object cast(@NotNull Object operand) {
1652           return operand;
1653         }
1654       }
1655       , new Caster() {
1656         @NotNull
1657         @Override
1658         public Object cast(@NotNull Object operand) {
1659           return (int)(Character)operand;
1660         }
1661       }
1662       , new Caster() {
1663         @NotNull
1664         @Override
1665         public Object cast(@NotNull Object operand) {
1666           return (long)(Character)operand;
1667         }
1668       }
1669       , new Caster() {
1670         @NotNull
1671         @Override
1672         public Object cast(@NotNull Object operand) {
1673           return (float)(Character)operand;
1674         }
1675       }
1676       , new Caster() {
1677         @NotNull
1678         @Override
1679         public Object cast(@NotNull Object operand) {
1680           return (double)(Character)operand;
1681         }
1682       }
1683     }
1684     ,
1685     {
1686       new Caster() {
1687         @NotNull
1688         @Override
1689         public Object cast(@NotNull Object operand) {
1690           return (byte)((Integer)operand).intValue();
1691         }
1692       }
1693       , new Caster() {
1694         @NotNull
1695         @Override
1696         public Object cast(@NotNull Object operand) {
1697           return (short)((Integer)operand).intValue();
1698         }
1699       }
1700       , new Caster() {
1701         @NotNull
1702         @Override
1703         public Object cast(@NotNull Object operand) {
1704           return (char)((Integer)operand).intValue();
1705         }
1706       }
1707       , new Caster() {
1708         @NotNull
1709         @Override
1710         public Object cast(@NotNull Object operand) {
1711           return operand;
1712         }
1713       }
1714       , new Caster() {
1715         @NotNull
1716         @Override
1717         public Object cast(@NotNull Object operand) {
1718           return (long)(Integer)operand;
1719         }
1720       }
1721       , new Caster() {
1722         @NotNull
1723         @Override
1724         public Object cast(@NotNull Object operand) {
1725           return (float)(Integer)operand;
1726         }
1727       }
1728       , new Caster() {
1729         @NotNull
1730         @Override
1731         public Object cast(@NotNull Object operand) {
1732           return (double)(Integer)operand;
1733         }
1734       }
1735     }
1736     ,
1737     {
1738       new Caster() {
1739         @NotNull
1740         @Override
1741         public Object cast(@NotNull Object operand) {
1742           return (byte)((Long)operand).longValue();
1743         }
1744       }
1745       , new Caster() {
1746         @NotNull
1747         @Override
1748         public Object cast(@NotNull Object operand) {
1749           return (short)((Long)operand).longValue();
1750         }
1751       }
1752       , new Caster() {
1753         @NotNull
1754         @Override
1755         public Object cast(@NotNull Object operand) {
1756           return (char)((Long)operand).longValue();
1757         }
1758       }
1759       , new Caster() {
1760         @NotNull
1761         @Override
1762         public Object cast(@NotNull Object operand) {
1763           return (int)((Long)operand).longValue();
1764         }
1765       }
1766       , new Caster() {
1767         @NotNull
1768         @Override
1769         public Object cast(@NotNull Object operand) {
1770           return operand;
1771         }
1772       }
1773       , new Caster() {
1774         @NotNull
1775         @Override
1776         public Object cast(@NotNull Object operand) {
1777           return (float)(Long)operand;
1778         }
1779       }
1780       , new Caster() {
1781         @NotNull
1782         @Override
1783         public Object cast(@NotNull Object operand) {
1784           return (double)(Long)operand;
1785         }
1786       }
1787     }
1788     ,
1789     {
1790       new Caster() {
1791         @NotNull
1792         @Override
1793         public Object cast(@NotNull Object operand) {
1794           return (byte)((Float)operand).floatValue();
1795         }
1796       }
1797       , new Caster() {
1798         @NotNull
1799         @Override
1800         public Object cast(@NotNull Object operand) {
1801           return (short)((Float)operand).floatValue();
1802         }
1803       }
1804       , new Caster() {
1805         @NotNull
1806         @Override
1807         public Object cast(@NotNull Object operand) {
1808           return (char)((Float)operand).floatValue();
1809         }
1810       }
1811       , new Caster() {
1812         @NotNull
1813         @Override
1814         public Object cast(@NotNull Object operand) {
1815           return (int)((Float)operand).floatValue();
1816         }
1817       }
1818       , new Caster() {
1819         @NotNull
1820         @Override
1821         public Object cast(@NotNull Object operand) {
1822           return (long)((Float)operand).floatValue();
1823         }
1824       }
1825       , new Caster() {
1826         @NotNull
1827         @Override
1828         public Object cast(@NotNull Object operand) {
1829           return operand;
1830         }
1831       }
1832       , new Caster() {
1833         @NotNull
1834         @Override
1835         public Object cast(@NotNull Object operand) {
1836           return (double)(Float)operand;
1837         }
1838       }
1839     }
1840     ,
1841     {
1842       new Caster() {
1843         @NotNull
1844         @Override
1845         public Object cast(@NotNull Object operand) {
1846           return (byte)((Double)operand).doubleValue();
1847         }
1848       }
1849       , new Caster() {
1850         @NotNull
1851         @Override
1852         public Object cast(@NotNull Object operand) {
1853           return (short)((Double)operand).doubleValue();
1854         }
1855       }
1856       , new Caster() {
1857         @NotNull
1858         @Override
1859         public Object cast(@NotNull Object operand) {
1860           return (char)((Double)operand).doubleValue();
1861         }
1862       }
1863       , new Caster() {
1864         @NotNull
1865         @Override
1866         public Object cast(@NotNull Object operand) {
1867           return (int)((Double)operand).doubleValue();
1868         }
1869       }
1870       , new Caster() {
1871         @NotNull
1872         @Override
1873         public Object cast(@NotNull Object operand) {
1874           return (long)((Double)operand).doubleValue();
1875         }
1876       }
1877       , new Caster() {
1878         @NotNull
1879         @Override
1880         public Object cast(@NotNull Object operand) {
1881           return new Float((Double)operand);
1882         }
1883       }
1884       , new Caster() {
1885         @NotNull
1886         @Override
1887         public Object cast(@NotNull Object operand) {
1888           return operand;
1889         }
1890       }
1891     }
1892   };
1893
1894   private static final Map<Class, PsiType> WRAPPER_TO_PRIMITIVE = new THashMap<Class, PsiType>(8);
1895   static {
1896     WRAPPER_TO_PRIMITIVE.put(Boolean.class, PsiType.BOOLEAN);
1897     WRAPPER_TO_PRIMITIVE.put(Byte.class, PsiType.BYTE);
1898     WRAPPER_TO_PRIMITIVE.put(Character.class, PsiType.CHAR);
1899     WRAPPER_TO_PRIMITIVE.put(Short.class, PsiType.SHORT);
1900     WRAPPER_TO_PRIMITIVE.put(Integer.class, PsiType.INT);
1901     WRAPPER_TO_PRIMITIVE.put(Long.class, PsiType.LONG);
1902     WRAPPER_TO_PRIMITIVE.put(Float.class, PsiType.FLOAT);
1903     WRAPPER_TO_PRIMITIVE.put(Double.class, PsiType.DOUBLE);
1904   }
1905
1906   private static PsiType wrapperToPrimitive(@NotNull Object o) {
1907     return WRAPPER_TO_PRIMITIVE.get(o.getClass());
1908   }
1909 }