suggest to delete type arguments when they are not expected (IDEA-79300)
[idea/community.git] / java / java-impl / src / com / intellij / codeInsight / daemon / impl / analysis / GenericsHighlightUtil.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.codeInsight.daemon.impl.analysis;
17
18 import com.google.common.collect.Lists;
19 import com.intellij.codeInsight.AnnotationUtil;
20 import com.intellij.codeInsight.daemon.JavaErrorMessages;
21 import com.intellij.codeInsight.daemon.impl.HighlightInfo;
22 import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
23 import com.intellij.codeInsight.daemon.impl.quickfix.*;
24 import com.intellij.codeInsight.intention.IntentionAction;
25 import com.intellij.codeInsight.intention.QuickFixFactory;
26 import com.intellij.openapi.diagnostic.Logger;
27 import com.intellij.openapi.project.IndexNotReadyException;
28 import com.intellij.openapi.project.Project;
29 import com.intellij.openapi.util.Comparing;
30 import com.intellij.openapi.util.TextRange;
31 import com.intellij.pom.java.LanguageLevel;
32 import com.intellij.psi.*;
33 import com.intellij.psi.search.GlobalSearchScope;
34 import com.intellij.psi.search.searches.ReferencesSearch;
35 import com.intellij.psi.search.searches.SuperMethodsSearch;
36 import com.intellij.psi.util.*;
37 import com.intellij.util.ArrayUtil;
38 import com.intellij.util.containers.HashMap;
39 import com.intellij.util.containers.HashSet;
40 import gnu.trove.THashMap;
41 import org.jetbrains.annotations.NonNls;
42 import org.jetbrains.annotations.NotNull;
43 import org.jetbrains.annotations.Nullable;
44
45 import java.util.*;
46
47 /**
48  * @author cdr
49  */
50 public class GenericsHighlightUtil {
51   private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.analysis.GenericsHighlightUtil");
52   private static final String GENERICS_ARE_NOT_SUPPORTED = JavaErrorMessages.message("generics.are.not.supported");
53   private static final QuickFixFactory QUICK_FIX_FACTORY = QuickFixFactory.getInstance();
54
55   private GenericsHighlightUtil() {}
56
57   public static HighlightInfo checkInferredTypeArguments(PsiMethod genericMethod,
58                                                          PsiMethodCallExpression call,
59                                                          PsiSubstitutor substitutor) {
60     PsiTypeParameter[] typeParameters = genericMethod.getTypeParameters();
61     for (PsiTypeParameter typeParameter : typeParameters) {
62       PsiType substituted = substitutor.substitute(typeParameter);
63       if (substituted == null) return null;
64       substituted = PsiUtil.captureToplevelWildcards(substituted, call);
65       PsiClassType[] extendsTypes = typeParameter.getExtendsListTypes();
66       for (PsiClassType type : extendsTypes) {
67         PsiType extendsType = substitutor.substitute(type);
68         if (!TypeConversionUtil.isAssignable(extendsType, substituted, false)) {
69           PsiClass boundClass = extendsType instanceof PsiClassType ? ((PsiClassType)extendsType).resolve() : null;
70
71           @NonNls String messageKey = boundClass == null || typeParameter.isInterface() == boundClass.isInterface()
72                                       ? "generics.inferred.type.for.type.parameter.is.not.within.its.bound.extend"
73                                       : "generics.inferred.type.for.type.parameter.is.not.within.its.bound.implement";
74
75           String description = JavaErrorMessages.message(
76             messageKey,
77             HighlightUtil.formatClass(typeParameter),
78             HighlightUtil.formatType(extendsType),
79             HighlightUtil.formatType(substituted)
80           );
81
82           return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, call, description);
83         }
84       }
85     }
86
87     return null;
88   }
89
90   public static HighlightInfo checkParameterizedReferenceTypeArguments(PsiElement resolved,
91                                                                        final PsiJavaCodeReferenceElement referenceElement,
92                                                                        final PsiSubstitutor substitutor) {
93     if (!(resolved instanceof PsiTypeParameterListOwner)) return null;
94     final PsiTypeParameterListOwner typeParameterListOwner = (PsiTypeParameterListOwner)resolved;
95     return checkReferenceTypeArgumentList(typeParameterListOwner, referenceElement.getParameterList(), substitutor, true);
96   }
97
98   public static HighlightInfo checkReferenceTypeArgumentList(final PsiTypeParameterListOwner typeParameterListOwner,
99                                                              final PsiReferenceParameterList referenceParameterList,
100                                                              final PsiSubstitutor substitutor,
101                                                              boolean registerIntentions) {
102     if (referenceParameterList != null && !PsiUtil.isLanguageLevel5OrHigher(referenceParameterList)) {
103       if (referenceParameterList.getTypeParameterElements().length > 0) {
104         HighlightInfo info = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, referenceParameterList, GENERICS_ARE_NOT_SUPPORTED);
105         QuickFixAction.registerQuickFixAction(info, new ShowModulePropertiesFix(referenceParameterList));
106         QuickFixAction.registerQuickFixAction(info, new IncreaseLanguageLevelFix(LanguageLevel.JDK_1_5));
107         return info;
108       }
109     }
110
111     PsiDiamondType.DiamondInferenceResult inferenceResult = null;
112     PsiTypeElement[] referenceElements = null;
113     if (referenceParameterList != null) {
114       referenceElements = referenceParameterList.getTypeParameterElements();
115       if (referenceElements.length == 1 && referenceElements[0].getType() instanceof PsiDiamondType) {
116         if (!typeParameterListOwner.hasTypeParameters()) {
117           return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, referenceElements[0], "Diamond operator is not applicable for non-parameterized types");
118         }
119         inferenceResult = ((PsiDiamondType)referenceElements[0].getType()).resolveInferredTypes();
120         final String errorMessage = inferenceResult.getErrorMessage();
121         if (errorMessage != null) {
122           final PsiType expectedType = detectExpectedType(referenceParameterList);
123           if (!(inferenceResult.failedToInfer() && expectedType instanceof PsiClassType && ((PsiClassType)expectedType).isRaw())) {
124             return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, referenceElements[0], errorMessage);
125           }
126         }
127       }
128     }
129
130     final PsiTypeParameter[] typeParameters = typeParameterListOwner.getTypeParameters();
131     final int targetParametersNum = typeParameters.length;
132     final int refParametersNum = referenceParameterList == null ? 0 : referenceParameterList.getTypeArguments().length;
133     if (targetParametersNum != refParametersNum && refParametersNum != 0) {
134       final String description;
135       if (targetParametersNum == 0) {
136         if (PsiTreeUtil.getParentOfType(referenceParameterList, PsiCall.class) != null &&
137             PsiUtil.isLanguageLevel7OrHigher(referenceParameterList)) {
138           description = null;
139         } else {
140           description = JavaErrorMessages.message(
141             "generics.type.or.method.does.not.have.type.parameters",
142             typeParameterListOwnerCategoryDescription(typeParameterListOwner),
143             typeParameterListOwnerDescription(typeParameterListOwner)
144           );
145         }
146       }
147       else {
148         description = JavaErrorMessages.message(
149           "generics.wrong.number.of.type.arguments", refParametersNum, targetParametersNum
150         );
151       }
152
153       if (description != null) {
154         final HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, referenceParameterList, description);
155         if (registerIntentions) {
156           PsiElement pparent = referenceParameterList.getParent().getParent();
157           if (pparent instanceof PsiTypeElement) {
158             PsiElement variable = pparent.getParent();
159             if (variable instanceof PsiVariable) {
160               if (targetParametersNum == 0) {
161                 QuickFixAction.registerQuickFixAction(highlightInfo, new RemoveTypeArgumentsFix(variable), null);
162               }
163               VariableParameterizedTypeFix.registerIntentions(highlightInfo, (PsiVariable)variable, referenceParameterList);
164             }
165           }
166         }
167         return highlightInfo;
168       }
169     }
170
171     // bounds check
172     if (targetParametersNum > 0 && refParametersNum != 0) {
173       if (inferenceResult != null) {
174         final PsiType[] types = inferenceResult.getTypes();
175         for (int i = 0; i < typeParameters.length; i++) {
176           final PsiType type = types[i];
177           final HighlightInfo highlightInfo = checkTypeParameterWithinItsBound(typeParameters[i], substitutor,  type, referenceElements[0]);
178           if (highlightInfo != null) return highlightInfo;
179         }
180       } else {
181         for (int i = 0; i < typeParameters.length; i++) {
182           final PsiTypeElement typeElement = referenceElements[i];
183           final HighlightInfo highlightInfo = checkTypeParameterWithinItsBound(typeParameters[i], substitutor, typeElement.getType(), typeElement);
184           if (highlightInfo != null) return highlightInfo;
185         }
186       }
187     }
188
189     return null;
190   }
191
192   private static PsiType detectExpectedType(PsiReferenceParameterList referenceParameterList) {
193     final PsiNewExpression newExpression = PsiTreeUtil.getParentOfType(referenceParameterList, PsiNewExpression.class);
194     LOG.assertTrue(newExpression != null);
195     final PsiElement parent = newExpression.getParent();
196     PsiType expectedType = null;
197     if (parent instanceof PsiVariable && newExpression.equals(((PsiVariable)parent).getInitializer())) {
198       expectedType = ((PsiVariable)parent).getType();
199     }
200     else if (parent instanceof PsiAssignmentExpression && newExpression.equals(((PsiAssignmentExpression)parent).getRExpression())) {
201       expectedType = ((PsiAssignmentExpression)parent).getLExpression().getType();
202     }
203     else if (parent instanceof PsiReturnStatement) {
204       PsiMethod method = PsiTreeUtil.getParentOfType(parent, PsiMethod.class);
205       if (method != null) {
206         expectedType = method.getReturnType();
207       }
208     }
209     else if (parent instanceof PsiExpressionList) {
210       final PsiElement pParent = parent.getParent();
211       if (pParent instanceof PsiCallExpression && parent.equals(((PsiCallExpression)pParent).getArgumentList())) {
212         final PsiMethod method = ((PsiCallExpression)pParent).resolveMethod();
213         if (method != null) {
214           final PsiExpression[] expressions = ((PsiCallExpression)pParent).getArgumentList().getExpressions();
215           final int idx = ArrayUtil.find(expressions, newExpression);
216           if (idx > -1) {
217             final PsiParameterList parameterList = method.getParameterList();
218             if (idx < parameterList.getParametersCount()) {
219               expectedType = parameterList.getParameters()[idx].getType();
220             }
221           }
222         }
223       }
224     }
225     return expectedType;
226   }
227
228   @Nullable
229   private static HighlightInfo checkTypeParameterWithinItsBound(final PsiTypeParameter classParameter,
230                                                                 final PsiSubstitutor substitutor,
231                                                                 final PsiType type,
232                                                                 final PsiElement typeElement2Highlight) {
233     final PsiClass referenceClass;
234     if (type instanceof PsiClassType){
235       referenceClass = ((PsiClassType)type).resolve();
236     } else {
237       referenceClass = null;
238     }
239     final PsiType psiType = substitutor.substitute(classParameter);
240     if (psiType instanceof PsiClassType && !(PsiUtil.resolveClassInType(psiType) instanceof PsiTypeParameter)) {
241       if (checkNotInBounds(type, psiType)) {
242         final String description = "Actual type argument and inferred type contradict each other";
243         return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, typeElement2Highlight, description);
244       }
245     }
246
247     final PsiClassType[] bounds = classParameter.getSuperTypes();
248     for (PsiClassType type1 : bounds) {
249       PsiType bound = substitutor.substitute(type1);
250       if (checkNotInBounds(type, bound)) {
251         PsiClass boundClass = bound instanceof PsiClassType ? ((PsiClassType)bound).resolve() : null;
252
253         @NonNls final String messageKey = boundClass == null || referenceClass == null || referenceClass.isInterface() == boundClass.isInterface()
254                                           ? "generics.type.parameter.is.not.within.its.bound.extend"
255                                           : "generics.type.parameter.is.not.within.its.bound.implement";
256
257         String description = JavaErrorMessages.message(messageKey,
258                                                        referenceClass != null ? HighlightUtil.formatClass(referenceClass) : type.getPresentableText(),
259                                                        HighlightUtil.formatType(bound));
260
261         final HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR,
262                                                                               typeElement2Highlight,
263                                                                               description);
264         if (bound instanceof PsiClassType && referenceClass != null) {
265           QuickFixAction
266             .registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createExtendsListFix(referenceClass, (PsiClassType)bound, true),
267                                     null);
268         }
269         return highlightInfo;
270       }
271     }
272     return null;
273   }
274
275   private static boolean checkNotInBounds(PsiType type, PsiType bound) {
276     if (type instanceof PsiClassType) {
277       return checkNotAssignable(bound, type);
278     }
279     else {
280       if (type instanceof PsiWildcardType) {
281         if (((PsiWildcardType)type).isExtends()) {
282           return checkExtendsWildcardCaptureFailure((PsiWildcardType)type, bound);
283         }
284         else if (((PsiWildcardType)type).isSuper()) {
285           return checkNotAssignable(bound, ((PsiWildcardType)type).getSuperBound());
286         }
287       }
288       else if (type instanceof PsiArrayType) {
289         return checkNotAssignable(bound, type, true);
290       }
291     }
292     return false;
293   }
294
295   //JLS 5.1.10
296   private static boolean checkExtendsWildcardCaptureFailure(PsiWildcardType type, PsiType bound) {
297     LOG.assertTrue(type.isExtends());
298     final PsiType extendsBound = type.getExtendsBound();
299     PsiType boundBound = bound;
300     if (bound instanceof PsiWildcardType) {
301       if (((PsiWildcardType)bound).isBounded()) {
302         boundBound = ((PsiWildcardType)bound).isSuper()
303                      ? ((PsiWildcardType)bound).getSuperBound()
304                      : ((PsiWildcardType)bound).getExtendsBound();
305       } else {
306         return false;
307       }
308     }
309     return !TypeConversionUtil.areTypesConvertible(boundBound, extendsBound) &&
310            !TypeConversionUtil.areTypesConvertible(extendsBound, boundBound);
311   }
312
313   private static boolean checkNotAssignable(final PsiType bound, final PsiType type) {
314     return checkNotAssignable(bound, type, allowUncheckedConversions(type));
315   }
316
317   private static boolean checkNotAssignable(final PsiType bound,
318                                             final PsiType type,
319                                             final boolean allowUncheckedConversion) {
320     if (bound instanceof PsiWildcardType) {
321       if (((PsiWildcardType)bound).isBounded()) {
322         final PsiType boundBound = ((PsiWildcardType)bound).isExtends()
323                                    ? ((PsiWildcardType)bound).getExtendsBound()
324                                    : ((PsiWildcardType)bound).getSuperBound();
325         return !TypeConversionUtil.isAssignable(boundBound, type, allowUncheckedConversion);
326       } else {
327         return true;
328       }
329     } else {
330       return !TypeConversionUtil.isAssignable(bound, type, allowUncheckedConversion);
331     }
332   }
333
334   private static boolean allowUncheckedConversions(PsiType type) {
335     boolean allowUncheckedConversions = true;
336     if (type instanceof PsiClassType) {
337       final PsiClass classType = ((PsiClassType)type).resolve();
338       if (classType != null) {
339         for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(classType)) {
340           allowUncheckedConversions &=  parameter.getExtendsListTypes().length == 0;
341         }
342       }
343     }
344     return allowUncheckedConversions;
345   }
346
347   private static String typeParameterListOwnerDescription(final PsiTypeParameterListOwner typeParameterListOwner) {
348     if (typeParameterListOwner instanceof PsiClass) {
349       return HighlightUtil.formatClass((PsiClass)typeParameterListOwner);
350     }
351     else if (typeParameterListOwner instanceof PsiMethod) {
352       return HighlightUtil.formatMethod((PsiMethod)typeParameterListOwner);
353     }
354     else {
355       LOG.error("Unknown " + typeParameterListOwner);
356       return "?";
357     }
358   }
359
360   private static String typeParameterListOwnerCategoryDescription(final PsiTypeParameterListOwner typeParameterListOwner) {
361     if (typeParameterListOwner instanceof PsiClass) {
362       return JavaErrorMessages.message("generics.holder.type");
363     }
364     else if (typeParameterListOwner instanceof PsiMethod) {
365       return JavaErrorMessages.message("generics.holder.method");
366     }
367     else {
368       LOG.error("Unknown " + typeParameterListOwner);
369       return "?";
370     }
371   }
372
373   public static HighlightInfo checkElementInTypeParameterExtendsList(PsiReferenceList referenceList, JavaResolveResult resolveResult, PsiElement element) {
374     PsiClass aClass = (PsiClass)referenceList.getParent();
375     final PsiJavaCodeReferenceElement[] referenceElements = referenceList.getReferenceElements();
376     PsiClass extendFrom = (PsiClass)resolveResult.getElement();
377     if (extendFrom == null) return null;
378     HighlightInfo errorResult = null;
379     if (!extendFrom.isInterface() && referenceElements.length != 0 && element != referenceElements[0]) {
380       final String description = HighlightClassUtil.INTERFACE_EXPECTED;
381       errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, element, description);
382       PsiClassType type =
383         JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createType(extendFrom, resolveResult.getSubstitutor());
384       QuickFixAction.registerQuickFixAction(errorResult, new MoveBoundClassToFrontFix(aClass, type), null);
385     }
386     else if (referenceElements.length != 0 && element != referenceElements[0] && referenceElements[0].resolve() instanceof PsiTypeParameter) {
387       final String description = JavaErrorMessages.message("type.parameter.cannot.be.followed.by.other.bounds");
388       errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, element, description);
389       PsiClassType type =
390         JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createType(extendFrom, resolveResult.getSubstitutor());
391       IntentionAction fix = QUICK_FIX_FACTORY.createExtendsListFix(aClass, type, false);
392       QuickFixAction.registerQuickFixAction(errorResult, fix, null);
393     }
394     return errorResult;
395   }
396   public static HighlightInfo checkInterfaceMultipleInheritance(PsiClass aClass) {
397     if (aClass instanceof PsiTypeParameter) return null;
398     final PsiClassType[] types = aClass.getSuperTypes();
399     if (types.length < 2) return null;
400     Map<PsiClass, PsiSubstitutor> inheritedClasses = new HashMap<PsiClass, PsiSubstitutor>();
401     final TextRange textRange = HighlightNamesUtil.getClassDeclarationTextRange(aClass);
402     return checkInterfaceMultipleInheritance(aClass,
403                                              PsiSubstitutor.EMPTY, inheritedClasses,
404                                              new HashSet<PsiClass>(), textRange);
405   }
406
407   private static HighlightInfo checkInterfaceMultipleInheritance(PsiClass aClass,
408                                                                  PsiSubstitutor derivedSubstitutor,
409                                                                  Map<PsiClass, PsiSubstitutor> inheritedClasses,
410                                                                  Set<PsiClass> visited,
411                                                                  TextRange textRange) {
412     final PsiClassType[] superTypes = aClass.getSuperTypes();
413     for (PsiClassType superType : superTypes) {
414       final PsiClassType.ClassResolveResult result = superType.resolveGenerics();
415       final PsiClass superClass = result.getElement();
416       if (superClass == null || visited.contains(superClass)) continue;
417       PsiSubstitutor superTypeSubstitutor = result.getSubstitutor();
418       superTypeSubstitutor = MethodSignatureUtil.combineSubstitutors(superTypeSubstitutor, derivedSubstitutor);
419
420       final PsiSubstitutor inheritedSubstitutor = inheritedClasses.get(superClass);
421       if (inheritedSubstitutor != null) {
422         final PsiTypeParameter[] typeParameters = superClass.getTypeParameters();
423         for (PsiTypeParameter typeParameter : typeParameters) {
424           PsiType type1 = inheritedSubstitutor.substitute(typeParameter);
425           PsiType type2 = superTypeSubstitutor.substitute(typeParameter);
426
427           if (!Comparing.equal(type1, type2)) {
428             String description = JavaErrorMessages.message("generics.cannot.be.inherited.with.different.type.arguments",
429                                                            HighlightUtil.formatClass(superClass),
430                                                            HighlightUtil.formatType(type1),
431                                                            HighlightUtil.formatType(type2));
432             return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, textRange, description);
433           }
434         }
435       }
436       inheritedClasses.put(superClass, superTypeSubstitutor);
437       visited.add(superClass);
438       final HighlightInfo highlightInfo = checkInterfaceMultipleInheritance(superClass, superTypeSubstitutor, inheritedClasses, visited, textRange);
439       visited.remove(superClass);
440
441       if (highlightInfo != null) return highlightInfo;
442     }
443     return null;
444   }
445
446   public static HighlightInfo checkOverrideEquivalentMethods(final PsiClass aClass) {
447     final Collection<HierarchicalMethodSignature> signaturesWithSupers = aClass.getVisibleSignatures();
448     PsiManager manager = aClass.getManager();
449     Map<MethodSignature, MethodSignatureBackedByPsiMethod> sameErasureMethods =
450       new THashMap<MethodSignature, MethodSignatureBackedByPsiMethod>(MethodSignatureUtil.METHOD_PARAMETERS_ERASURE_EQUALITY);
451                                                                        
452     for (HierarchicalMethodSignature signature : signaturesWithSupers) {
453       HighlightInfo info = checkSameErasureNotSubSignatureInner(signature, manager, aClass, sameErasureMethods);
454       if (info != null) return info;
455     }
456
457     return null;
458   }
459
460   @Nullable
461   private static HighlightInfo checkSameErasureNotSubSignatureInner(final HierarchicalMethodSignature signature,
462                                                                     final PsiManager manager,
463                                                                     final PsiClass aClass,
464                                                                     final Map<MethodSignature, MethodSignatureBackedByPsiMethod> sameErasureMethods) {
465     PsiMethod method = signature.getMethod();
466     JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
467     if (!facade.getResolveHelper().isAccessible(method, aClass, null)) return null;
468     MethodSignature signatureToErase = method.getSignature(PsiSubstitutor.EMPTY);
469     MethodSignatureBackedByPsiMethod sameErasure = sameErasureMethods.get(signatureToErase);
470     HighlightInfo info;
471     if (sameErasure != null) {
472       info = checkSameErasureNotSubSignatureOrSameClass(sameErasure, signature, aClass, method);
473       if (info != null) return info;
474     }
475     else {
476       sameErasureMethods.put(signatureToErase, signature);
477     }
478     List<HierarchicalMethodSignature> supers = signature.getSuperSignatures();
479     for (HierarchicalMethodSignature superSignature : supers) {
480       info = checkSameErasureNotSubSignatureInner(superSignature, manager, aClass, sameErasureMethods);
481       if (info != null) return info;
482     }
483     return null;
484   }
485
486   @Nullable
487   private static HighlightInfo checkSameErasureNotSubSignatureOrSameClass(final MethodSignatureBackedByPsiMethod signatureToCheck,
488                                                                           final HierarchicalMethodSignature superSignature,
489                                                                           final PsiClass aClass,
490                                                                           final PsiMethod superMethod) {
491     final PsiMethod checkMethod = signatureToCheck.getMethod();
492     if (superMethod.equals(checkMethod)) return null;
493     PsiClass checkContainingClass = checkMethod.getContainingClass();
494     LOG.assertTrue(checkContainingClass != null);
495     PsiClass superContainingClass = superMethod.getContainingClass();
496     boolean checkEqualsSuper = checkContainingClass.equals(superContainingClass);
497     if (checkMethod.isConstructor()) {
498       if (!superMethod.isConstructor() || !checkEqualsSuper) return null;
499     }
500     else if (superMethod.isConstructor()) return null;
501
502     if (checkMethod.hasModifierProperty(PsiModifier.STATIC) && !checkEqualsSuper) {
503       return null;
504     }
505
506     final PsiType retErasure1 = TypeConversionUtil.erasure(checkMethod.getReturnType());
507     final PsiType retErasure2 = TypeConversionUtil.erasure(superMethod.getReturnType());
508     if (!Comparing.equal(retErasure1, retErasure2) &&
509         !TypeConversionUtil.isVoidType(retErasure1) &&
510         !TypeConversionUtil.isVoidType(retErasure2) &&
511         !(checkEqualsSuper && Arrays.equals(superSignature.getParameterTypes(), signatureToCheck.getParameterTypes()))) {
512       return null;
513     }
514
515     if (!checkEqualsSuper && MethodSignatureUtil.isSubsignature(superSignature, signatureToCheck)) {
516       return null;
517     }
518     if (superContainingClass != null && !superContainingClass.isInterface() && checkContainingClass.isInterface() && !aClass.equals(superContainingClass)) return null;
519     if (aClass.equals(checkContainingClass)) {
520       boolean sameClass = aClass.equals(superContainingClass);
521       return getSameErasureMessage(sameClass, checkMethod, superMethod, HighlightNamesUtil.getMethodDeclarationTextRange(checkMethod));
522     }
523     else {
524       return getSameErasureMessage(false, checkMethod, superMethod, HighlightNamesUtil.getClassDeclarationTextRange(aClass));
525     }
526   }
527
528   private static HighlightInfo getSameErasureMessage(final boolean sameClass, final PsiMethod method, final PsiMethod superMethod,
529                                                      TextRange textRange) {
530     @NonNls final String key = sameClass ? "generics.methods.have.same.erasure" : "generics.methods.have.same.erasure.override";
531     String description = JavaErrorMessages.message(key, HighlightMethodUtil.createClashMethodMessage(method, superMethod, !sameClass));
532     return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, textRange, description);
533   }
534
535   public static HighlightInfo checkTypeParameterInstantiation(PsiNewExpression expression) {
536     PsiJavaCodeReferenceElement classReference = expression.getClassOrAnonymousClassReference();
537     if (classReference == null) return null;
538     final JavaResolveResult result = classReference.advancedResolve(false);
539     final PsiElement element = result.getElement();
540     if (element instanceof PsiTypeParameter) {
541       String description = JavaErrorMessages.message("generics.type.parameter.cannot.be.instantiated",
542                                                      HighlightUtil.formatClass((PsiTypeParameter)element));
543       return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, classReference, description);
544     }
545     return null;
546   }
547
548   public static HighlightInfo checkWildcardUsage(PsiTypeElement typeElement) {
549     PsiType type = typeElement.getType();
550     if (type instanceof PsiWildcardType) {
551       if (typeElement.getParent() instanceof PsiReferenceParameterList) {
552         PsiElement parent = typeElement.getParent().getParent();
553         LOG.assertTrue(parent instanceof PsiJavaCodeReferenceElement, parent);
554         PsiElement refParent = parent.getParent();
555         if (refParent instanceof PsiAnonymousClass) refParent = refParent.getParent();
556         if (refParent instanceof PsiNewExpression) {
557           PsiNewExpression newExpression = (PsiNewExpression)refParent;
558           if (!(newExpression.getType() instanceof PsiArrayType)) {
559             String description = JavaErrorMessages.message("wildcard.type.cannot.be.instantiated", HighlightUtil.formatType(type));
560             return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, typeElement, description);
561           }
562         }
563         else if (refParent instanceof PsiReferenceList) {
564           PsiElement refPParent = refParent.getParent();
565           if (!(refPParent instanceof PsiTypeParameter) || refParent != ((PsiTypeParameter)refPParent).getExtendsList()) {
566             return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR,
567                                                      typeElement,
568                                                      JavaErrorMessages.message("generics.wildcard.not.expected"));
569           }
570         }
571       }
572       else {
573         return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR,
574                                                  typeElement,
575                                                  JavaErrorMessages.message("generics.wildcards.may.be.used.only.as.reference.parameters"));
576       }
577     }
578
579     return null;
580   }
581
582   public static HighlightInfo checkReferenceTypeUsedAsTypeArgument(PsiTypeElement typeElement) {
583     final PsiType type = typeElement.getType();
584     if (type != PsiType.NULL && type instanceof PsiPrimitiveType ||
585         type instanceof PsiWildcardType && ((PsiWildcardType)type).getBound() instanceof PsiPrimitiveType) {
586       final PsiElement element = new PsiMatcherImpl(typeElement)
587         .parent(PsiMatchers.hasClass(PsiReferenceParameterList.class))
588         .parent(PsiMatchers.hasClass(PsiJavaCodeReferenceElement.class))
589         .getElement();
590       if (element == null) return null;
591
592       String description = JavaErrorMessages.message("generics.type.argument.cannot.be.of.primitive.type");
593       return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, typeElement, description);
594     }
595
596     return null;
597   }
598
599   public static boolean isRawToGeneric(PsiType lType, PsiType rType) {
600     if (lType instanceof PsiPrimitiveType || rType instanceof PsiPrimitiveType) return false;
601     if (lType.equals(rType)) return false;
602     if (lType instanceof PsiArrayType && rType instanceof PsiArrayType) {
603       return isRawToGeneric(((PsiArrayType)lType).getComponentType(), ((PsiArrayType)rType).getComponentType());
604     }
605     if (lType instanceof PsiArrayType || rType instanceof PsiArrayType) return false;
606
607     if (rType instanceof PsiIntersectionType) {
608       for (PsiType type : ((PsiIntersectionType)rType).getConjuncts()) {
609         if (isRawToGeneric(lType, type)) return true;
610       }
611       return false;
612     } else if (lType instanceof PsiIntersectionType) {
613       for (PsiType type : ((PsiIntersectionType)lType).getConjuncts()) {
614         if (isRawToGeneric(type, rType)) return true;
615       }
616       return false;
617     }
618
619     if (rType instanceof PsiDisjunctionType || lType instanceof PsiDisjunctionType) {
620       return false;
621     }
622
623     if (lType instanceof PsiCapturedWildcardType || rType instanceof PsiCapturedWildcardType) {
624       return false;
625     }
626
627     if (lType instanceof PsiWildcardType || rType instanceof PsiWildcardType) return false;
628
629     boolean isValidType = lType instanceof PsiClassType && rType instanceof PsiClassType;
630     if (!isValidType) {
631       LOG.error("Invalid types: rType =" + rType + ", lType=" + lType);
632     }
633     PsiClassType.ClassResolveResult lResolveResult = ((PsiClassType)lType).resolveGenerics();
634     PsiClassType.ClassResolveResult rResolveResult = ((PsiClassType)rType).resolveGenerics();
635     PsiClass lClass = lResolveResult.getElement();
636     PsiClass rClass = rResolveResult.getElement();
637
638     if (rClass instanceof PsiAnonymousClass) {
639       return isRawToGeneric(lType, ((PsiAnonymousClass)rClass).getBaseClassType());
640     }
641
642     PsiSubstitutor lSubstitutor = lResolveResult.getSubstitutor();
643     PsiSubstitutor rSubstitutor = rResolveResult.getSubstitutor();
644     if (lClass == null || rClass == null) return false;
645     if (lClass instanceof PsiTypeParameter &&
646         !InheritanceUtil.isInheritorOrSelf(rClass, lClass, true)) return true;
647
648     if (!lClass.getManager().areElementsEquivalent(lClass, rClass)) {
649       if (lClass.isInheritor(rClass, true)) {
650         lSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(rClass, lClass, lSubstitutor);
651         lClass = rClass;
652       }
653       else if (rClass.isInheritor(lClass, true)) {
654         rSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(lClass, rClass, rSubstitutor);
655         rClass = lClass;
656       }
657       else {
658         return false;
659       }
660     }
661
662     Iterator<PsiTypeParameter> lIterator = PsiUtil.typeParametersIterator(lClass);
663     Iterator<PsiTypeParameter> rIterator = PsiUtil.typeParametersIterator(rClass);
664     while (lIterator.hasNext()) {
665       if (!rIterator.hasNext()) return false;
666       PsiTypeParameter lParameter = lIterator.next();
667       PsiTypeParameter rParameter = rIterator.next();
668       PsiType lTypeArg = lSubstitutor.substitute(lParameter);
669       PsiType rTypeArg = rSubstitutor.substituteWithBoundsPromotion(rParameter);
670       if (lTypeArg == null) continue;
671       if (rTypeArg == null) {
672         if (lTypeArg instanceof PsiWildcardType && ((PsiWildcardType) lTypeArg).getBound() == null) {
673           continue;
674         }
675         else {
676           return true;
677         }
678       }
679       if (isUncheckedTypeArgumentConversion(lTypeArg, rTypeArg)) return true;
680     }
681     return false;
682   }
683
684   public static boolean isUncheckedCast(PsiType castType, PsiType operandType) {
685     if (TypeConversionUtil.isAssignable(castType, operandType, false)) return false;
686
687     castType = castType.getDeepComponentType();
688     if (castType instanceof PsiClassType) {
689       final PsiClassType castClassType = (PsiClassType)castType;
690       operandType = operandType.getDeepComponentType();
691
692       if (!(operandType instanceof PsiClassType)) return false;
693       final PsiClassType operandClassType = (PsiClassType)operandType;
694       final PsiClassType.ClassResolveResult castResult = castClassType.resolveGenerics();
695       final PsiClassType.ClassResolveResult operandResult = operandClassType.resolveGenerics();
696       final PsiClass operandClass = operandResult.getElement();
697       final PsiClass castClass = castResult.getElement();
698
699       if (operandClass == null || castClass == null) return false;
700       if (castClass instanceof PsiTypeParameter) return true;
701
702       if (castClassType.hasNonTrivialParameters()) {
703         if (operandClassType.isRaw()) return true;
704         if (castClass.isInheritor(operandClass, true)) {
705           PsiSubstitutor castSubstitutor = castResult.getSubstitutor();
706           for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(castClass)) {
707             PsiSubstitutor modifiedSubstitutor = castSubstitutor.put(typeParameter, null);
708             PsiClassType otherType =
709               JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(castClass, modifiedSubstitutor);
710             if (TypeConversionUtil.isAssignable(operandType, otherType, false)) return true;
711           }
712           return false;
713         }
714         return true;
715       }
716     }
717
718     return false;
719   }
720
721   private static boolean isUncheckedTypeArgumentConversion (PsiType lTypeArg, PsiType rTypeArg) {
722     if (lTypeArg instanceof PsiPrimitiveType || rTypeArg instanceof PsiPrimitiveType) return false;
723     if (lTypeArg.equals(rTypeArg)) return false;
724     if (lTypeArg instanceof PsiCapturedWildcardType) {
725       //ignore capture conversion
726       return isUncheckedTypeArgumentConversion(((PsiCapturedWildcardType)lTypeArg).getWildcard(), rTypeArg);
727     }
728     if (rTypeArg instanceof PsiCapturedWildcardType) {
729       //ignore capture conversion
730       return isUncheckedTypeArgumentConversion(lTypeArg, ((PsiCapturedWildcardType)rTypeArg).getWildcard());
731     }
732
733     if (lTypeArg instanceof PsiWildcardType || rTypeArg instanceof PsiWildcardType) {
734       return !lTypeArg.isAssignableFrom(rTypeArg);
735     }
736
737     if (lTypeArg instanceof PsiArrayType && rTypeArg instanceof PsiArrayType) {
738       return isUncheckedTypeArgumentConversion(((PsiArrayType)rTypeArg).getComponentType(), ((PsiArrayType)lTypeArg).getComponentType());
739     }
740     if (lTypeArg instanceof PsiArrayType || rTypeArg instanceof PsiArrayType) return false;
741     if (lTypeArg instanceof PsiIntersectionType) {
742       for (PsiType type : ((PsiIntersectionType)lTypeArg).getConjuncts()) {
743         if (!isUncheckedTypeArgumentConversion(type, rTypeArg)) return false;
744       }
745       return true;
746     }
747     if (!(lTypeArg instanceof PsiClassType)) {
748       LOG.error("left: "+lTypeArg + "; "+lTypeArg.getClass());
749     }
750     if (rTypeArg instanceof PsiIntersectionType) {
751       for (PsiType type : ((PsiIntersectionType)rTypeArg).getConjuncts()) {
752         if (!isUncheckedTypeArgumentConversion(lTypeArg, type)) return false;
753       }
754       return true;
755     }
756     if (!(rTypeArg instanceof PsiClassType)) {
757       LOG.error("right :"+rTypeArg + "; "+rTypeArg.getClass());
758     }
759     return ((PsiClassType)lTypeArg).resolve() instanceof PsiTypeParameter ||
760            ((PsiClassType)rTypeArg).resolve() instanceof PsiTypeParameter;
761   }
762
763   @Nullable
764   public static HighlightInfo checkForeachLoopParameterType(PsiForeachStatement statement) {
765     final PsiParameter parameter = statement.getIterationParameter();
766     final PsiExpression expression = statement.getIteratedValue();
767     if (expression == null || expression.getType() == null) return null;
768     final PsiType itemType = getCollectionItemType(expression);
769     if (itemType == null) {
770       String description = JavaErrorMessages.message("foreach.not.applicable",
771                                                      HighlightUtil.formatType(expression.getType()));
772       return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, description);
773     }
774     final int start = parameter.getTextRange().getStartOffset();
775     final int end = expression.getTextRange().getEndOffset();
776     final PsiType parameterType = parameter.getType();
777     HighlightInfo highlightInfo = HighlightUtil.checkAssignability(parameterType, itemType, null, new TextRange(start, end));
778     if (highlightInfo != null) {
779       HighlightUtil.registerChangeVariableTypeFixes(parameter, itemType, highlightInfo);
780     }
781     return highlightInfo;
782   }
783
784   @Nullable
785   public static PsiType getCollectionItemType(@NotNull PsiExpression expression) {
786     final PsiType type = expression.getType();
787     if (type == null) return null;
788     if (type instanceof PsiArrayType) {
789       return ((PsiArrayType)type).getComponentType();
790     }
791     if (type instanceof PsiClassType) {
792       final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)type).resolveGenerics();
793       PsiClass aClass = resolveResult.getElement();
794       if (aClass == null) return null;
795       final PsiManager manager = aClass.getManager();
796       final String qName = aClass.getQualifiedName();
797       PsiSubstitutor substitutor = resolveResult.getSubstitutor();
798       JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
799       if (qName != null) {
800         PsiClass myClass = facade.findClass(qName, expression.getResolveScope());
801         if (myClass != null && myClass != aClass) {
802           //different JDKs
803           PsiTypeParameter thisTypeParameter = getIterableTypeParameter(facade, myClass);
804           if (thisTypeParameter == null) return null;
805           PsiTypeParameter thatTypeParameter = getIterableTypeParameter(facade, aClass);
806           if (thatTypeParameter != null) { //it can be null if we reference collection in JDK1.4 module from JDK5 source
807             substitutor = substitutor.put(thisTypeParameter, substitutor.substitute(thatTypeParameter));
808           }
809           aClass = myClass;
810         }
811       }
812       PsiTypeParameter typeParameter = getIterableTypeParameter(facade, aClass);
813       if (typeParameter == null) return null;
814       PsiClass owner = (PsiClass)typeParameter.getOwner();
815       if (owner == null) return null;
816       PsiSubstitutor superClassSubstitutor = TypeConversionUtil.getClassSubstitutor(owner, aClass, PsiSubstitutor.EMPTY);
817       if (superClassSubstitutor == null) return null;
818       PsiType itemType = superClassSubstitutor.substitute(typeParameter);
819       itemType = substitutor.substitute(itemType);
820       return itemType == null ? PsiType.getJavaLangObject(manager, aClass.getResolveScope()) : itemType;
821     }
822     return null;
823   }
824
825   @Nullable
826   private static PsiTypeParameter getIterableTypeParameter(final JavaPsiFacade facade, final PsiClass context) {
827     PsiClass iterable = facade.findClass("java.lang.Iterable", context.getResolveScope());
828     if (iterable == null) return null;
829     PsiTypeParameter[] typeParameters = iterable.getTypeParameters();
830     if (typeParameters.length != 1) return null;
831     return typeParameters[0];
832   }
833
834   @Nullable
835   public static HighlightInfo checkAccessStaticFieldFromEnumConstructor(PsiReferenceExpression expr, JavaResolveResult result) {
836     final PsiElement resolved = result.getElement();
837
838     if (!(resolved instanceof PsiField)) return null;
839     if (!((PsiModifierListOwner)resolved).hasModifierProperty(PsiModifier.STATIC)) return null;
840     final PsiMember constructorOrInitializer = PsiUtil.findEnclosingConstructorOrInitializer(expr);
841     if (constructorOrInitializer == null) return null;
842     if (constructorOrInitializer.hasModifierProperty(PsiModifier.STATIC)) return null;
843     final PsiClass aClass = constructorOrInitializer.getContainingClass();
844     if (aClass == null) return null;
845     if (!aClass.isEnum()) return null;
846     final PsiField field = (PsiField)resolved;
847     if (field.getContainingClass() != aClass) return null;
848     final PsiType type = field.getType();
849
850     //TODO is access to enum constant is allowed ?
851     if (type instanceof PsiClassType && ((PsiClassType)type).resolve() == aClass) return null;
852
853     if (PsiUtil.isCompileTimeConstant(field)) return null;
854
855     String description = JavaErrorMessages.message(
856       "illegal.to.access.static.member.from.enum.constructor.or.instance.initializer",
857       HighlightMessageUtil.getSymbolName(resolved, result.getSubstitutor())
858     );
859
860     return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expr, description);
861   }
862
863   @Nullable
864   public static HighlightInfo checkEnumInstantiation(PsiNewExpression expression) {
865     final PsiType type = expression.getType();
866     if (type instanceof PsiClassType) {
867       final PsiClass aClass = ((PsiClassType)type).resolve();
868       if (aClass != null && aClass.isEnum()) {
869         String description = JavaErrorMessages.message("enum.types.cannot.be.instantiated");
870         return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expression, description);
871       }
872     }
873     return null;
874   }
875
876   @Nullable
877   public static HighlightInfo checkGenericArrayCreation(PsiElement element, PsiType type) {
878     if (type instanceof PsiArrayType) {
879       PsiType componentType = type.getDeepComponentType();
880       if (componentType instanceof PsiClassType) {
881         final PsiClassType classType = (PsiClassType)componentType;
882         PsiType[] parameters = classType.getParameters();
883         for (PsiType parameter : parameters) {
884           if (!(parameter instanceof PsiWildcardType) || ((PsiWildcardType)parameter).getBound() != null) {
885             return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR,
886                                                      element,
887                                                      JavaErrorMessages.message("generic.array.creation"));
888           }
889         }
890         final PsiClass resolved = ((PsiClassType)PsiUtil.convertAnonymousToBaseType(classType)).resolve();
891         if (resolved instanceof PsiTypeParameter) {
892           return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR,
893                                                    element,
894                                                    JavaErrorMessages.message("generic.array.creation"));
895         }
896       }
897     }
898
899     return null;
900   }
901
902   private static final MethodSignature ourValuesEnumSyntheticMethod = MethodSignatureUtil.createMethodSignature("values",
903                                                                                                                 PsiType.EMPTY_ARRAY,
904                                                                                                                 PsiTypeParameter.EMPTY_ARRAY,
905                                                                                                                 PsiSubstitutor.EMPTY);
906
907   public static boolean isEnumSyntheticMethod(MethodSignature methodSignature, Project project) {
908     if (methodSignature.equals(ourValuesEnumSyntheticMethod)) return true;
909     final PsiType javaLangString = PsiType.getJavaLangString(PsiManager.getInstance(project), GlobalSearchScope.allScope(project));
910     final MethodSignature valueOfMethod = MethodSignatureUtil.createMethodSignature("valueOf", new PsiType[]{javaLangString}, PsiTypeParameter.EMPTY_ARRAY,
911                                                                                     PsiSubstitutor.EMPTY);
912     return valueOfMethod.equals(methodSignature);
913   }
914
915   public static HighlightInfo checkTypeParametersList(PsiTypeParameterList parameterList) {
916     PsiTypeParameter[] typeParameters = parameterList.getTypeParameters();
917     if (typeParameters.length == 0) return null;
918     if (!PsiUtil.isLanguageLevel5OrHigher(parameterList)) {
919       HighlightInfo info = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, parameterList, GENERICS_ARE_NOT_SUPPORTED);
920       QuickFixAction.registerQuickFixAction(info, new ShowModulePropertiesFix(parameterList));
921       QuickFixAction.registerQuickFixAction(info, new IncreaseLanguageLevelFix(LanguageLevel.JDK_1_5));
922       return info;
923     }
924     final PsiElement parent = parameterList.getParent();
925     if (parent instanceof PsiClass && ((PsiClass)parent).isEnum()) {
926       return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR,
927                                                parameterList,
928                                                JavaErrorMessages.message("generics.enum.may.not.have.type.parameters"));
929     }
930     if (parent instanceof PsiAnnotationMethod) {
931       return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, parameterList, JavaErrorMessages.message("generics.annotation.members.may.not.have.type.parameters"));
932     }
933     else if (parent instanceof PsiClass && ((PsiClass)parent).isAnnotationType()) {
934       return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, parameterList, JavaErrorMessages.message("annotation.may.not.have.type.parameters"));
935     }
936
937     for (int i = 0; i < typeParameters.length; i++) {
938       final PsiTypeParameter typeParameter1 = typeParameters[i];
939       String name1 = typeParameter1.getName();
940       for (int j = i+1; j < typeParameters.length; j++) {
941         final PsiTypeParameter typeParameter2 = typeParameters[j];
942         String name2 = typeParameter2.getName();
943         if (Comparing.strEqual(name1, name2)) {
944           String message = JavaErrorMessages.message("generics.duplicate.type.parameter", name1);
945           return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, typeParameter2, message);
946         }
947       }
948     }
949     return null;
950   }
951
952   @Nullable
953   public static Collection<HighlightInfo> checkCatchParameterIsClass(PsiParameter parameter) {
954     if (!(parameter.getDeclarationScope() instanceof PsiCatchSection)) return null;
955     final Collection<HighlightInfo> result = Lists.newArrayList();
956
957     final List<PsiTypeElement> typeElements = PsiUtil.getParameterTypeElements(parameter);
958     for (PsiTypeElement typeElement : typeElements) {
959       final PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(typeElement.getType());
960       if (aClass instanceof PsiTypeParameter) {
961         final String message = JavaErrorMessages.message("generics.cannot.catch.type.parameters");
962         result.add(HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, typeElement, message));
963       }
964     }
965
966     return result;
967   }
968
969   public static HighlightInfo checkInstanceOfGenericType(PsiInstanceOfExpression expression) {
970     final PsiTypeElement checkTypeElement = expression.getCheckType();
971     if (checkTypeElement == null) return null;
972     PsiElement ref = checkTypeElement.getInnermostComponentReferenceElement();
973     while (ref instanceof PsiJavaCodeReferenceElement) {
974       final HighlightInfo result = isIllegalForInstanceOf((PsiJavaCodeReferenceElement)ref, checkTypeElement);
975       if (result != null) return result;
976       ref = ((PsiQualifiedReference)ref).getQualifier();
977     }
978     return null;
979   }
980
981   private static HighlightInfo isIllegalForInstanceOf(PsiJavaCodeReferenceElement ref, final PsiTypeElement typeElement) {
982     final PsiElement resolved = ref.resolve();
983     if (resolved instanceof PsiTypeParameter) {
984       return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, ref, JavaErrorMessages.message("generics.cannot.instanceof.type.parameters"));
985     }
986
987     final PsiType[] parameters = ref.getTypeParameters();
988     for (PsiType parameterType : parameters) {
989       if (parameterType != null &&
990           !(parameterType instanceof PsiWildcardType && ((PsiWildcardType)parameterType).getBound() == null)) {
991          return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, typeElement, JavaErrorMessages.message("illegal.generic.type.for.instanceof"));
992       }
993     }
994
995     return null;
996   }
997
998   public static HighlightInfo checkClassObjectAccessExpression(PsiClassObjectAccessExpression expression) {
999     PsiType type = expression.getOperand().getType();
1000     if (type instanceof PsiClassType) {
1001       PsiClass aClass = ((PsiClassType)type).resolve();
1002       if (aClass instanceof PsiTypeParameter) {
1003         return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR,
1004                                                  expression.getOperand(),
1005                                                  JavaErrorMessages.message("cannot.select.dot.class.from.type.variable"));
1006       }
1007     }
1008
1009     return null;
1010   }
1011
1012   @Nullable
1013   public static HighlightInfo checkOverrideAnnotation(PsiMethod method) {
1014     PsiModifierList list = method.getModifierList();
1015     final PsiAnnotation overrideAnnotation = list.findAnnotation("java.lang.Override");
1016     if (overrideAnnotation == null) {
1017       return null;
1018     }
1019     try {
1020       MethodSignatureBackedByPsiMethod superMethod = SuperMethodsSearch.search(method, null, true, false).findFirst();
1021       if (superMethod == null) {
1022         HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, overrideAnnotation,
1023                                                                         JavaErrorMessages.message("method.doesnot.override.super"));
1024         PullAsAbstractUpFix.registerQuickFix(highlightInfo, method);
1025         return highlightInfo;
1026       }
1027       LanguageLevel languageLevel = PsiUtil.getLanguageLevel(method);
1028       PsiClass superClass = superMethod.getMethod().getContainingClass();
1029       if (languageLevel.equals(LanguageLevel.JDK_1_5) &&
1030           superClass != null &&
1031           superClass.isInterface()) {
1032         HighlightInfo info = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, overrideAnnotation, JavaErrorMessages.message("override.not.allowed.in.interfaces"));
1033         QuickFixAction.registerQuickFixAction(info, new IncreaseLanguageLevelFix(LanguageLevel.JDK_1_6));
1034         return info;
1035       }
1036       return null;
1037     }
1038     catch (IndexNotReadyException e) {
1039       return null;
1040     }
1041   }
1042
1043   @Nullable
1044   public static HighlightInfo checkSafeVarargsAnnotation(PsiMethod method) {
1045     PsiModifierList list = method.getModifierList();
1046     final PsiAnnotation safeVarargsAnnotation = list.findAnnotation("java.lang.SafeVarargs");
1047     if (safeVarargsAnnotation == null) {
1048       return null;
1049     }
1050     try {
1051       if (!method.isVarArgs()) {
1052         return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, safeVarargsAnnotation, "@SafeVarargs is not allowed on methods with fixed arity");
1053       }
1054       if (!method.hasModifierProperty(PsiModifier.STATIC) && !method.hasModifierProperty(PsiModifier.FINAL) && !method.isConstructor()) {
1055         return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, safeVarargsAnnotation, "@SafeVarargs is not allowed on non-final instance methods");
1056       }
1057
1058       final PsiParameter varParameter = method.getParameterList().getParameters()[method.getParameterList().getParametersCount() - 1];
1059
1060       for (PsiReference reference : ReferencesSearch.search(varParameter)) {
1061         final PsiElement element = reference.getElement();
1062         if (element instanceof PsiExpression && !PsiUtil.isAccessedForReading((PsiExpression)element)) {
1063           return HighlightInfo.createHighlightInfo(HighlightInfoType.WARNING, element, "@SafeVarargs do not suppress potentially unsafe operations");
1064         }
1065       }
1066
1067
1068
1069       LOG.assertTrue(varParameter.isVarArgs());
1070       final PsiEllipsisType ellipsisType = (PsiEllipsisType)varParameter.getType();
1071       final PsiType componentType = ellipsisType.getComponentType();
1072       if (isReifiableType(componentType)) {
1073         return HighlightInfo.createHighlightInfo(HighlightInfoType.WARNING, varParameter.getTypeElement(), "@SafeVarargs is not applicable for reifiable types");
1074       }
1075       return null;
1076     }
1077     catch (IndexNotReadyException e) {
1078       return null;
1079     }
1080   }
1081
1082   public static boolean isUncheckedWarning(PsiJavaCodeReferenceElement expression, PsiElement resolve) {
1083     if (resolve instanceof PsiMethod) {
1084       final PsiMethod psiMethod = (PsiMethod)resolve;
1085
1086       final LanguageLevel languageLevel = PsiUtil.getLanguageLevel(expression);
1087
1088       if (psiMethod.isVarArgs()) {
1089         if (!languageLevel.isAtLeast(LanguageLevel.JDK_1_7) || !AnnotationUtil.isAnnotated(psiMethod, "java.lang.SafeVarargs", false)) {
1090           final int parametersCount = psiMethod.getParameterList().getParametersCount();
1091           final PsiParameter varargParameter =
1092             psiMethod.getParameterList().getParameters()[parametersCount - 1];
1093           final PsiType componentType = ((PsiEllipsisType)varargParameter.getType()).getComponentType();
1094           if (!isReifiableType(componentType)) {
1095             final PsiElement parent = expression.getParent();
1096             if (parent instanceof PsiCall) {
1097               final PsiExpressionList argumentList = ((PsiCall)parent).getArgumentList();
1098               if (argumentList != null) {
1099                 final PsiExpression[] args = argumentList.getExpressions();
1100                 if (args.length == parametersCount) {
1101                   final PsiExpression lastArg = args[args.length - 1];
1102                   if (lastArg instanceof PsiReferenceExpression) {
1103                     final PsiElement lastArgsResolve = ((PsiReferenceExpression)lastArg).resolve();
1104                     if (lastArgsResolve instanceof PsiParameter) {
1105                       if (((PsiParameter)lastArgsResolve).getType() instanceof PsiArrayType) {
1106                         return false;
1107                       }
1108                     }
1109                   } else if (lastArg instanceof PsiMethodCallExpression) {
1110                     if (lastArg.getType() instanceof PsiArrayType) {
1111                       return false;
1112                     }
1113                   }
1114                 }
1115                 for (int i = parametersCount - 1; i < args.length; i++) {
1116                   if (!isReifiableType(args[i].getType())){
1117                     return true;
1118                   }
1119                 }
1120                 return args.length < parametersCount;
1121               }
1122             }
1123           }
1124         }
1125       }
1126     }
1127     return false;
1128   }
1129
1130   public static boolean isReifiableType(PsiType type) {
1131     if (type instanceof PsiArrayType) {
1132       return isReifiableType(((PsiArrayType)type).getComponentType());
1133     }
1134
1135     if (type instanceof PsiPrimitiveType) {
1136       return true;
1137     }
1138
1139     if (PsiUtil.resolveClassInType(type) instanceof PsiTypeParameter) {
1140       return false;
1141     }
1142
1143     if (type instanceof PsiClassType) {
1144       final PsiClassType classType = (PsiClassType)PsiUtil.convertAnonymousToBaseType(type);
1145       if (classType.isRaw()) {
1146         return true;
1147       }
1148       PsiType[] parameters = classType.getParameters();
1149
1150       for (PsiType parameter : parameters) {
1151         if (parameter instanceof PsiWildcardType && ((PsiWildcardType)parameter).getBound() == null) {
1152           return true;
1153         }
1154       }
1155       final PsiClass resolved = ((PsiClassType)PsiUtil.convertAnonymousToBaseType(classType)).resolve();
1156       if (resolved instanceof PsiTypeParameter) {
1157         return false;
1158       }
1159       return parameters.length == 0;
1160     }
1161
1162     return false;
1163   }
1164
1165   static void checkEnumConstantForConstructorProblems(PsiEnumConstant enumConstant, final HighlightInfoHolder holder) {
1166     PsiClass containingClass = enumConstant.getContainingClass();                      if (enumConstant.getInitializingClass() == null) {
1167       HighlightInfo highlightInfo = HighlightClassUtil.checkInstantiationOfAbstractClass(containingClass, enumConstant.getNameIdentifier());
1168       if (highlightInfo != null) {
1169         QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createImplementMethodsFix(enumConstant));
1170         holder.add(highlightInfo);
1171         return;
1172       }
1173       highlightInfo = HighlightClassUtil.checkClassWithAbstractMethods(enumConstant.getContainingClass(), enumConstant.getNameIdentifier().getTextRange());
1174       if (highlightInfo != null) {
1175         holder.add(highlightInfo);
1176         return;
1177       }
1178     }
1179     PsiClassType type = JavaPsiFacade.getInstance(enumConstant.getProject()).getElementFactory().createType(containingClass);
1180
1181     HighlightMethodUtil.checkConstructorCall(type.resolveGenerics(), enumConstant, type, null, holder);
1182   }
1183
1184   @Nullable
1185   public static HighlightInfo checkEnumSuperConstructorCall(PsiMethodCallExpression expr) {
1186     PsiReferenceExpression methodExpression = expr.getMethodExpression();
1187     final PsiElement refNameElement = methodExpression.getReferenceNameElement();
1188     if (refNameElement != null && PsiKeyword.SUPER.equals(refNameElement.getText())) {
1189       final PsiMember constructor = PsiUtil.findEnclosingConstructorOrInitializer(expr);
1190       if (constructor instanceof PsiMethod) {
1191         final PsiClass aClass = constructor.getContainingClass();
1192         if (aClass != null && aClass.isEnum()) {
1193           final String message = JavaErrorMessages.message("call.to.super.is.not.allowed.in.enum.constructor");
1194           return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, expr, message);
1195         }
1196       }
1197     }
1198     return null;
1199   }
1200
1201   public static HighlightInfo checkVarArgParameterIsLast(PsiParameter parameter) {
1202     PsiElement declarationScope = parameter.getDeclarationScope();
1203     if (declarationScope instanceof PsiMethod) {
1204       PsiParameter[] params = ((PsiMethod)declarationScope).getParameterList().getParameters();
1205       if (parameter.isVarArgs()) {
1206         if (!PsiUtil.getLanguageLevel(parameter).hasEnumKeywordAndAutoboxing()) {
1207           HighlightInfo info = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, parameter, JavaErrorMessages.message("varargs.prior.15"));
1208           QuickFixAction.registerQuickFixAction(info, new IncreaseLanguageLevelFix(LanguageLevel.JDK_1_5));
1209           return info;
1210         }
1211
1212         if (params[params.length - 1] != parameter) {
1213           HighlightInfo info = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, parameter,
1214                                                                  JavaErrorMessages.message("vararg.not.last.parameter"));
1215           QuickFixAction.registerQuickFixAction(info, new MakeVarargParameterLastFix(parameter), null);
1216           return info;
1217         }
1218       }
1219     }
1220     return null;
1221   }
1222
1223   public static List<HighlightInfo> checkEnumConstantModifierList(PsiModifierList modifierList) {
1224     List<HighlightInfo> list = null;
1225     PsiElement[] children = modifierList.getChildren();
1226     for (PsiElement child : children) {
1227       if (child instanceof PsiKeyword) {
1228         if (list == null) {
1229           list = new ArrayList<HighlightInfo>();
1230         }
1231         list.add(HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR,
1232                                                    child,
1233                                                    JavaErrorMessages.message("modifiers.for.enum.constants")));
1234       }
1235     }
1236     return list;
1237   }
1238
1239   public static HighlightInfo checkParametersOnRaw(PsiReferenceParameterList refParamList) {
1240     if (refParamList.getTypeArguments().length == 0) return null;
1241     JavaResolveResult resolveResult = null;
1242     PsiElement parent = refParamList.getParent();
1243     if (parent instanceof PsiJavaCodeReferenceElement) {
1244       resolveResult = ((PsiJavaCodeReferenceElement)parent).advancedResolve(false);
1245     } else if (parent instanceof PsiCallExpression) {
1246       resolveResult =  ((PsiCallExpression)parent).resolveMethodGenerics();
1247     }
1248     if (resolveResult != null) {
1249       PsiElement element = resolveResult.getElement();
1250       if (!(element instanceof PsiTypeParameterListOwner)) return null;
1251       if (((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.STATIC)) return null;
1252       PsiClass containingClass = ((PsiMember)element).getContainingClass();
1253       if (containingClass != null && PsiUtil.isRawSubstitutor(containingClass, resolveResult.getSubstitutor())) {
1254         if (parent instanceof PsiCallExpression && PsiUtil.isLanguageLevel7OrHigher(parent)) {
1255           return null;
1256         }
1257         final String message = element instanceof PsiClass
1258                                ? JavaErrorMessages.message("generics.type.arguments.on.raw.type")
1259                                : JavaErrorMessages.message("generics.type.arguments.on.raw.method");
1260
1261         return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, refParamList, message);
1262       }
1263     }
1264     return null;
1265   }
1266
1267   public static HighlightInfo checkCannotInheritFromEnum(PsiClass superClass, PsiElement elementToHighlight) {
1268     HighlightInfo errorResult = null;
1269     if (Comparing.strEqual("java.lang.Enum",superClass.getQualifiedName())) {
1270       String message = JavaErrorMessages.message("classes.extends.enum");
1271       errorResult = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, elementToHighlight, message);
1272     }
1273     return errorResult;
1274   }
1275   public static HighlightInfo checkGenericCannotExtendException(PsiReferenceList list) {
1276     PsiElement parent = list.getParent();
1277     if (!(parent instanceof PsiClass)) return null;
1278     PsiClass aClass = (PsiClass)parent;
1279
1280     if (!aClass.hasTypeParameters() || aClass.getExtendsList() != list) return null;
1281     PsiJavaCodeReferenceElement[] referenceElements = list.getReferenceElements();
1282     PsiClass throwableClass = null;
1283     for (PsiJavaCodeReferenceElement referenceElement : referenceElements) {
1284       PsiElement resolved = referenceElement.resolve();
1285       if (!(resolved instanceof PsiClass)) continue;
1286       if (throwableClass == null) {
1287         throwableClass = JavaPsiFacade.getInstance(aClass.getProject()).findClass("java.lang.Throwable", aClass.getResolveScope());
1288       }
1289       if (InheritanceUtil.isInheritorOrSelf((PsiClass)resolved, throwableClass, true)) {
1290         String message = JavaErrorMessages.message("generic.extend.exception");
1291         HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, referenceElement, message);
1292         PsiClassType classType = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createType((PsiClass)resolved);
1293         QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createExtendsListFix(aClass, classType, false));
1294         return highlightInfo;
1295       }
1296     }
1297     return null;
1298   }
1299
1300   public static HighlightInfo checkEnumMustNotBeLocal(final PsiClass aClass) {
1301     if (!aClass.isEnum()) return null;
1302     PsiElement parent = aClass.getParent();
1303     if (!(parent instanceof PsiClass || parent instanceof PsiFile)) {
1304       return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR,
1305                                                HighlightNamesUtil.getClassDeclarationTextRange(aClass),
1306                                                JavaErrorMessages.message("local.enum"));
1307     }
1308     return null;
1309   }
1310
1311   public static HighlightInfo checkSelectStaticClassFromParameterizedType(final PsiElement resolved, final PsiJavaCodeReferenceElement ref) {
1312     if (resolved instanceof PsiClass && ((PsiClass)resolved).hasModifierProperty(PsiModifier.STATIC)) {
1313       final PsiElement qualifier = ref.getQualifier();
1314       if (qualifier instanceof PsiJavaCodeReferenceElement) {
1315         final PsiReferenceParameterList parameterList = ((PsiJavaCodeReferenceElement)qualifier).getParameterList();
1316         if (parameterList != null && parameterList.getTypeArguments().length > 0) {
1317           final String message = JavaErrorMessages.message("generics.select.static.class.from.parameterized.type",
1318                                                            HighlightUtil.formatClass((PsiClass)resolved));
1319           return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, parameterList, message);
1320         }
1321       }
1322     }
1323     return null;
1324   }
1325
1326   public static HighlightInfo checkCannotInheritFromTypeParameter(final PsiClass superClass, final PsiJavaCodeReferenceElement toHighlight) {
1327     if (superClass instanceof PsiTypeParameter) {
1328       return HighlightInfo.createHighlightInfo(HighlightInfoType.ERROR, toHighlight,
1329                                                JavaErrorMessages.message("class.cannot.inherit.from.its.type.parameter"));
1330     }
1331     return null;
1332   }
1333 }
1334