1b54bb0767ba3e716a9bb7b23ebd5197c6858dc0
[idea/community.git] / java / java-analysis-impl / src / com / intellij / codeInsight / daemon / impl / analysis / GenericsHighlightUtil.java
1 /*
2  * Copyright 2000-2016 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.intellij.codeInsight.daemon.JavaErrorMessages;
19 import com.intellij.codeInsight.daemon.impl.HighlightInfo;
20 import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
21 import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
22 import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixActionRegistrarImpl;
23 import com.intellij.codeInsight.intention.QuickFixFactory;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.project.DumbService;
26 import com.intellij.openapi.project.IndexNotReadyException;
27 import com.intellij.openapi.project.Project;
28 import com.intellij.openapi.projectRoots.JavaSdkVersion;
29 import com.intellij.openapi.projectRoots.JavaVersionService;
30 import com.intellij.openapi.roots.FileIndexFacade;
31 import com.intellij.openapi.util.Comparing;
32 import com.intellij.openapi.util.Pair;
33 import com.intellij.openapi.util.TextRange;
34 import com.intellij.openapi.vfs.VirtualFile;
35 import com.intellij.pom.java.LanguageLevel;
36 import com.intellij.psi.*;
37 import com.intellij.psi.impl.PsiClassImplUtil;
38 import com.intellij.psi.search.GlobalSearchScope;
39 import com.intellij.psi.search.PsiShortNamesCache;
40 import com.intellij.psi.search.searches.ReferencesSearch;
41 import com.intellij.psi.search.searches.SuperMethodsSearch;
42 import com.intellij.psi.util.*;
43 import com.intellij.util.ArrayUtilRt;
44 import com.intellij.util.containers.ContainerUtil;
45 import com.intellij.util.containers.HashMap;
46 import com.intellij.util.containers.HashSet;
47 import gnu.trove.THashMap;
48 import gnu.trove.THashSet;
49 import org.jetbrains.annotations.NonNls;
50 import org.jetbrains.annotations.NotNull;
51 import org.jetbrains.annotations.Nullable;
52
53 import java.util.*;
54
55 /**
56  * @author cdr
57  */
58 public class GenericsHighlightUtil {
59   private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.analysis.GenericsHighlightUtil");
60
61   private static final QuickFixFactory QUICK_FIX_FACTORY = QuickFixFactory.getInstance();
62
63   private GenericsHighlightUtil() { }
64
65   @Nullable
66   static HighlightInfo checkInferredTypeArguments(PsiTypeParameterListOwner listOwner,
67                                                   PsiElement call,
68                                                   PsiSubstitutor substitutor) {
69     return checkInferredTypeArguments(listOwner.getTypeParameters(), call, substitutor);
70   }
71
72   @Nullable
73   private static HighlightInfo checkInferredTypeArguments(PsiTypeParameter[] typeParameters,
74                                                           PsiElement call,
75                                                           PsiSubstitutor substitutor) {
76     final Pair<PsiTypeParameter, PsiType> inferredTypeArgument = GenericsUtil.findTypeParameterWithBoundError(typeParameters, substitutor,
77                                                                                                               call, false);
78     if (inferredTypeArgument != null) {
79       final PsiType extendsType = inferredTypeArgument.second;
80       final PsiTypeParameter typeParameter = inferredTypeArgument.first;
81       PsiClass boundClass = extendsType instanceof PsiClassType ? ((PsiClassType)extendsType).resolve() : null;
82
83       @NonNls String messageKey = boundClass == null || typeParameter.isInterface() == boundClass.isInterface()
84                                   ? "generics.inferred.type.for.type.parameter.is.not.within.its.bound.extend"
85                                   : "generics.inferred.type.for.type.parameter.is.not.within.its.bound.implement";
86
87       String description = JavaErrorMessages.message(
88         messageKey,
89         HighlightUtil.formatClass(typeParameter),
90         JavaHighlightUtil.formatType(extendsType),
91         JavaHighlightUtil.formatType(substitutor.substitute(typeParameter))
92       );
93       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(call).descriptionAndTooltip(description).create();
94     }
95
96     return null;
97   }
98
99   @Nullable
100   static HighlightInfo checkParameterizedReferenceTypeArguments(final PsiElement resolved,
101                                                                 final PsiJavaCodeReferenceElement referenceElement,
102                                                                 final PsiSubstitutor substitutor,
103                                                                 @NotNull JavaSdkVersion javaSdkVersion) {
104     if (!(resolved instanceof PsiTypeParameterListOwner)) return null;
105     final PsiTypeParameterListOwner typeParameterListOwner = (PsiTypeParameterListOwner)resolved;
106     return checkReferenceTypeArgumentList(typeParameterListOwner, referenceElement.getParameterList(), substitutor, true, javaSdkVersion);
107   }
108
109   @Nullable
110   static HighlightInfo checkReferenceTypeArgumentList(final PsiTypeParameterListOwner typeParameterListOwner,
111                                                       final PsiReferenceParameterList referenceParameterList,
112                                                       final PsiSubstitutor substitutor,
113                                                       boolean registerIntentions,
114                                                       @NotNull JavaSdkVersion javaSdkVersion) {
115     PsiDiamondType.DiamondInferenceResult inferenceResult = null;
116     PsiTypeElement[] referenceElements = null;
117     if (referenceParameterList != null) {
118       referenceElements = referenceParameterList.getTypeParameterElements();
119       if (referenceElements.length == 1 && referenceElements[0].getType() instanceof PsiDiamondType) {
120         if (!typeParameterListOwner.hasTypeParameters()) {
121           final String description = JavaErrorMessages.message("generics.diamond.not.applicable");
122           return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(referenceParameterList).descriptionAndTooltip(description).create();
123         }
124         inferenceResult = ((PsiDiamondType)referenceElements[0].getType()).resolveInferredTypes();
125         final String errorMessage = inferenceResult.getErrorMessage();
126         if (errorMessage != null) {
127           final PsiType expectedType = detectExpectedType(referenceParameterList);
128           if (!(inferenceResult.failedToInfer() && expectedType instanceof PsiClassType && ((PsiClassType)expectedType).isRaw())) {
129             return HighlightInfo
130               .newHighlightInfo(HighlightInfoType.ERROR).range(referenceParameterList).descriptionAndTooltip(errorMessage).create();
131           }
132         }
133       }
134     }
135
136     final PsiTypeParameter[] typeParameters = typeParameterListOwner.getTypeParameters();
137     final int targetParametersNum = typeParameters.length;
138     final int refParametersNum = referenceParameterList == null ? 0 : referenceParameterList.getTypeArguments().length;
139     if (targetParametersNum != refParametersNum && refParametersNum != 0) {
140       final String description;
141       if (targetParametersNum == 0) {
142         if (PsiTreeUtil.getParentOfType(referenceParameterList, PsiCall.class) != null &&
143             typeParameterListOwner instanceof PsiMethod &&
144             (javaSdkVersion.isAtLeast(JavaSdkVersion.JDK_1_7) || hasSuperMethodsWithTypeParams((PsiMethod)typeParameterListOwner))) {
145           description = null;
146         }
147         else {
148           description = JavaErrorMessages.message(
149             "generics.type.or.method.does.not.have.type.parameters",
150             typeParameterListOwnerCategoryDescription(typeParameterListOwner),
151             typeParameterListOwnerDescription(typeParameterListOwner)
152           );
153         }
154       }
155       else {
156         description = JavaErrorMessages.message("generics.wrong.number.of.type.arguments", refParametersNum, targetParametersNum);
157       }
158
159       if (description != null) {
160         final HighlightInfo highlightInfo =
161           HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(referenceParameterList).descriptionAndTooltip(description).create();
162         if (registerIntentions) {
163           if (typeParameterListOwner instanceof PsiClass) {
164             QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createChangeClassSignatureFromUsageFix((PsiClass)typeParameterListOwner, referenceParameterList));
165           }
166
167           PsiElement grandParent = referenceParameterList.getParent().getParent();
168           if (grandParent instanceof PsiTypeElement) {
169             PsiElement variable = grandParent.getParent();
170             if (variable instanceof PsiVariable) {
171               if (targetParametersNum == 0) {
172                 QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createRemoveTypeArgumentsFix(variable));
173               }
174               registerVariableParameterizedTypeFixes(highlightInfo, (PsiVariable)variable, referenceParameterList, javaSdkVersion);
175             }
176           }
177         }
178         return highlightInfo;
179       }
180     }
181
182     // bounds check
183     if (targetParametersNum > 0 && refParametersNum != 0) {
184       if (inferenceResult != null) {
185         final PsiType[] types = inferenceResult.getTypes();
186         for (int i = 0; i < typeParameters.length; i++) {
187           final PsiType type = types[i];
188           final HighlightInfo highlightInfo = checkTypeParameterWithinItsBound(typeParameters[i], substitutor, type, referenceElements[0], referenceParameterList);
189           if (highlightInfo != null) return highlightInfo;
190         }
191       }
192       else {
193         for (int i = 0; i < typeParameters.length; i++) {
194           final PsiTypeElement typeElement = referenceElements[i];
195           final HighlightInfo highlightInfo = checkTypeParameterWithinItsBound(typeParameters[i], substitutor, typeElement.getType(), typeElement, referenceParameterList);
196           if (highlightInfo != null) return highlightInfo;
197         }
198       }
199     }
200
201     return null;
202   }
203
204   private static boolean hasSuperMethodsWithTypeParams(PsiMethod method) {
205     for (PsiMethod superMethod : method.findDeepestSuperMethods()) {
206       if (superMethod.hasTypeParameters()) return true;
207     }
208     return false;
209   }
210
211   private static PsiType detectExpectedType(PsiReferenceParameterList referenceParameterList) {
212     final PsiNewExpression newExpression = PsiTreeUtil.getParentOfType(referenceParameterList, PsiNewExpression.class);
213     LOG.assertTrue(newExpression != null);
214     final PsiElement parent = newExpression.getParent();
215     PsiType expectedType = null;
216     if (parent instanceof PsiVariable && newExpression.equals(((PsiVariable)parent).getInitializer())) {
217       expectedType = ((PsiVariable)parent).getType();
218     }
219     else if (parent instanceof PsiAssignmentExpression && newExpression.equals(((PsiAssignmentExpression)parent).getRExpression())) {
220       expectedType = ((PsiAssignmentExpression)parent).getLExpression().getType();
221     }
222     else if (parent instanceof PsiReturnStatement) {
223       PsiElement method = PsiTreeUtil.getParentOfType(parent, PsiMethod.class, PsiLambdaExpression.class);
224       if (method instanceof PsiMethod) {
225         expectedType = ((PsiMethod)method).getReturnType();
226       }
227     }
228     else if (parent instanceof PsiExpressionList) {
229       final PsiElement pParent = parent.getParent();
230       if (pParent instanceof PsiCallExpression && parent.equals(((PsiCallExpression)pParent).getArgumentList())) {
231         final PsiMethod method = ((PsiCallExpression)pParent).resolveMethod();
232         if (method != null) {
233           final PsiExpression[] expressions = ((PsiCallExpression)pParent).getArgumentList().getExpressions();
234           final int idx = ArrayUtilRt.find(expressions, newExpression);
235           if (idx > -1) {
236             final PsiParameterList parameterList = method.getParameterList();
237             if (idx < parameterList.getParametersCount()) {
238               expectedType = parameterList.getParameters()[idx].getType();
239             }
240           }
241         }
242       }
243     }
244     return expectedType;
245   }
246
247   @Nullable
248   private static HighlightInfo checkTypeParameterWithinItsBound(PsiTypeParameter classParameter,
249                                                                 final PsiSubstitutor substitutor,
250                                                                 final PsiType type,
251                                                                 final PsiElement typeElement2Highlight,
252                                                                 PsiReferenceParameterList referenceParameterList) {
253     final PsiClass referenceClass = type instanceof PsiClassType ? ((PsiClassType)type).resolve() : null;
254     final PsiType psiType = substitutor.substitute(classParameter);
255     if (psiType instanceof PsiClassType && !(PsiUtil.resolveClassInType(psiType) instanceof PsiTypeParameter)) {
256       if (GenericsUtil.checkNotInBounds(type, psiType, referenceParameterList)) {
257         final String description = "Actual type argument and inferred type contradict each other";
258         return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement2Highlight).descriptionAndTooltip(description).create();
259       }
260     }
261
262     final PsiClassType[] bounds = classParameter.getSuperTypes();
263     for (PsiClassType type1 : bounds) {
264       PsiType bound = substitutor.substitute(type1);
265       if (!bound.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) && GenericsUtil.checkNotInBounds(type, bound, referenceParameterList)) {
266         PsiClass boundClass = bound instanceof PsiClassType ? ((PsiClassType)bound).resolve() : null;
267
268         @NonNls final String messageKey = boundClass == null || referenceClass == null || referenceClass.isInterface() == boundClass.isInterface()
269                                           ? "generics.type.parameter.is.not.within.its.bound.extend"
270                                           : "generics.type.parameter.is.not.within.its.bound.implement";
271
272         String description = JavaErrorMessages.message(messageKey,
273                                                        referenceClass != null ? HighlightUtil.formatClass(referenceClass) : type.getPresentableText(),
274                                                        JavaHighlightUtil.formatType(bound));
275
276         final HighlightInfo info =
277           HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement2Highlight).descriptionAndTooltip(description).create();
278         if (bound instanceof PsiClassType && referenceClass != null && info != null) {
279           QuickFixAction
280             .registerQuickFixAction(info, QUICK_FIX_FACTORY.createExtendsListFix(referenceClass, (PsiClassType)bound, true),
281                                     null);
282         }
283         return info;
284       }
285     }
286     return null;
287   }
288
289   private static String typeParameterListOwnerDescription(final PsiTypeParameterListOwner typeParameterListOwner) {
290     if (typeParameterListOwner instanceof PsiClass) {
291       return HighlightUtil.formatClass((PsiClass)typeParameterListOwner);
292     }
293     else if (typeParameterListOwner instanceof PsiMethod) {
294       return JavaHighlightUtil.formatMethod((PsiMethod)typeParameterListOwner);
295     }
296     else {
297       LOG.error("Unknown " + typeParameterListOwner);
298       return "?";
299     }
300   }
301
302   private static String typeParameterListOwnerCategoryDescription(final PsiTypeParameterListOwner typeParameterListOwner) {
303     if (typeParameterListOwner instanceof PsiClass) {
304       return JavaErrorMessages.message("generics.holder.type");
305     }
306     else if (typeParameterListOwner instanceof PsiMethod) {
307       return JavaErrorMessages.message("generics.holder.method");
308     }
309     else {
310       LOG.error("Unknown " + typeParameterListOwner);
311       return "?";
312     }
313   }
314
315   @Nullable
316   static HighlightInfo checkElementInTypeParameterExtendsList(@NotNull PsiReferenceList referenceList,
317                                                               @NotNull PsiClass aClass,
318                                                               @NotNull JavaResolveResult resolveResult,
319                                                               @NotNull PsiElement element) {
320     final PsiJavaCodeReferenceElement[] referenceElements = referenceList.getReferenceElements();
321     PsiClass extendFrom = (PsiClass)resolveResult.getElement();
322     if (extendFrom == null) return null;
323     HighlightInfo errorResult = null;
324     if (!extendFrom.isInterface() && referenceElements.length != 0 && element != referenceElements[0]) {
325       String description = JavaErrorMessages.message("interface.expected");
326       errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create();
327       PsiClassType type =
328         JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createType(extendFrom, resolveResult.getSubstitutor());
329       QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createMoveBoundClassToFrontFix(aClass, type), null);
330     }
331     else if (referenceElements.length != 0 && element != referenceElements[0] && referenceElements[0].resolve() instanceof PsiTypeParameter) {
332       final String description = JavaErrorMessages.message("type.parameter.cannot.be.followed.by.other.bounds");
333       errorResult = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create();
334       PsiClassType type =
335         JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createType(extendFrom, resolveResult.getSubstitutor());
336       QuickFixAction.registerQuickFixAction(errorResult, QUICK_FIX_FACTORY.createExtendsListFix(aClass, type, false), null);
337     }
338     return errorResult;
339   }
340
341   static HighlightInfo checkInterfaceMultipleInheritance(PsiClass aClass) {
342     final PsiClassType[] types = aClass.getSuperTypes();
343     if (types.length < 2) return null;
344     Map<PsiClass, PsiSubstitutor> inheritedClasses = new HashMap<>();
345     final TextRange textRange = HighlightNamesUtil.getClassDeclarationTextRange(aClass);
346     return checkInterfaceMultipleInheritance(aClass,
347                                              aClass,
348                                              PsiSubstitutor.EMPTY, inheritedClasses,
349                                              new HashSet<>(), textRange);
350   }
351
352   private static HighlightInfo checkInterfaceMultipleInheritance(PsiClass aClass,
353                                                                  PsiElement place, 
354                                                                  PsiSubstitutor derivedSubstitutor,
355                                                                  Map<PsiClass, PsiSubstitutor> inheritedClasses,
356                                                                  Set<PsiClass> visited,
357                                                                  TextRange textRange) {
358     final List<PsiClassType.ClassResolveResult> superTypes = PsiClassImplUtil.getScopeCorrectedSuperTypes(aClass, place.getResolveScope());
359     for (PsiClassType.ClassResolveResult result : superTypes) {
360       final PsiClass superClass = result.getElement();
361       if (superClass == null || visited.contains(superClass)) continue;
362       PsiSubstitutor superTypeSubstitutor = result.getSubstitutor();
363       superTypeSubstitutor = MethodSignatureUtil.combineSubstitutors(superTypeSubstitutor, derivedSubstitutor);
364
365       final PsiSubstitutor inheritedSubstitutor = inheritedClasses.get(superClass);
366       if (inheritedSubstitutor != null) {
367         final PsiTypeParameter[] typeParameters = superClass.getTypeParameters();
368         for (PsiTypeParameter typeParameter : typeParameters) {
369           PsiType type1 = inheritedSubstitutor.substitute(typeParameter);
370           PsiType type2 = superTypeSubstitutor.substitute(typeParameter);
371
372           if (!Comparing.equal(type1, type2)) {
373             String description = JavaErrorMessages.message("generics.cannot.be.inherited.with.different.type.arguments",
374                                                            HighlightUtil.formatClass(superClass),
375                                                            JavaHighlightUtil.formatType(type1),
376                                                            JavaHighlightUtil.formatType(type2));
377             return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
378           }
379         }
380       }
381       inheritedClasses.put(superClass, superTypeSubstitutor);
382       visited.add(superClass);
383       final HighlightInfo highlightInfo = checkInterfaceMultipleInheritance(superClass, place, superTypeSubstitutor, inheritedClasses, visited, textRange);
384       visited.remove(superClass);
385
386       if (highlightInfo != null) return highlightInfo;
387     }
388     return null;
389   }
390
391   static Collection<HighlightInfo> checkOverrideEquivalentMethods(@NotNull PsiClass aClass) {
392     List<HighlightInfo> result = new ArrayList<>();
393     final Collection<HierarchicalMethodSignature> signaturesWithSupers = aClass.getVisibleSignatures();
394     PsiManager manager = aClass.getManager();
395     Map<MethodSignature, MethodSignatureBackedByPsiMethod> sameErasureMethods =
396       new THashMap<>(MethodSignatureUtil.METHOD_PARAMETERS_ERASURE_EQUALITY);
397
398     final Set<MethodSignature> foundProblems = new THashSet<>(MethodSignatureUtil.METHOD_PARAMETERS_ERASURE_EQUALITY);
399     for (HierarchicalMethodSignature signature : signaturesWithSupers) {
400       HighlightInfo info = checkSameErasureNotSubSignatureInner(signature, manager, aClass, sameErasureMethods);
401       if (info != null && foundProblems.add(signature)) {
402         result.add(info);
403       }
404       if (aClass instanceof PsiTypeParameter) {
405         info = HighlightMethodUtil.checkMethodIncompatibleReturnType(signature, signature.getSuperSignatures(), true, HighlightNamesUtil.getClassDeclarationTextRange(aClass));
406         if (info != null) {
407           result.add(info);
408         }
409       }
410     }
411
412     return result.isEmpty() ? null : result;
413   }
414
415   static HighlightInfo checkDefaultMethodOverrideEquivalentToObjectNonPrivate(@NotNull LanguageLevel languageLevel,
416                                                                               @NotNull PsiClass aClass,
417                                                                               @NotNull PsiMethod method,
418                                                                               @NotNull PsiElement methodIdentifier) {
419     if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && aClass.isInterface() && method.hasModifierProperty(PsiModifier.DEFAULT)) {
420       HierarchicalMethodSignature sig = method.getHierarchicalMethodSignature();
421       for (HierarchicalMethodSignature methodSignature : sig.getSuperSignatures()) {
422         final PsiMethod objectMethod = methodSignature.getMethod();
423         final PsiClass containingClass = objectMethod.getContainingClass();
424         if (containingClass != null && CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName()) && objectMethod.hasModifierProperty(PsiModifier.PUBLIC)) {
425           return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
426             .descriptionAndTooltip("Default method '" + sig.getName() + "' overrides a member of 'java.lang.Object'")
427             .range(methodIdentifier)
428             .create();
429         }
430       }
431     }
432     return null;
433   }
434
435   static HighlightInfo checkUnrelatedDefaultMethods(@NotNull PsiClass aClass,
436                                                     @NotNull PsiIdentifier classIdentifier) {
437     final Map<? extends MethodSignature, Set<PsiMethod>> overrideEquivalent = PsiSuperMethodUtil.collectOverrideEquivalents(aClass);
438
439     final boolean isInterface = aClass.isInterface();
440     for (Set<PsiMethod> overrideEquivalentMethods : overrideEquivalent.values()) {
441       if (overrideEquivalentMethods.size() <= 1) continue;
442       List<PsiMethod> defaults = null;
443       List<PsiMethod> abstracts = null;
444       boolean hasConcrete = false;
445       for (PsiMethod method : overrideEquivalentMethods) {
446         final boolean isDefault = method.hasModifierProperty(PsiModifier.DEFAULT);
447         final boolean isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT);
448         if (isDefault) {
449           if (defaults == null) defaults = new ArrayList<>(2);
450           defaults.add(method);
451         }
452         if (isAbstract) {
453           if (abstracts == null) abstracts = new ArrayList<>(2);
454           abstracts.add(method);
455         }
456         hasConcrete |= !isDefault && !isAbstract;
457       }
458
459       if (!hasConcrete && defaults != null) {
460         final PsiMethod defaultMethod = defaults.get(0);
461         final PsiClass defaultMethodContainingClass = defaultMethod.getContainingClass();
462         if (defaultMethodContainingClass == null) continue;
463         final PsiMethod unrelatedMethod = abstracts != null ? abstracts.get(0) : defaults.get(1);
464         final PsiClass unrelatedMethodContainingClass = unrelatedMethod.getContainingClass();
465         if (unrelatedMethodContainingClass == null) continue;
466         if (!aClass.hasModifierProperty(PsiModifier.ABSTRACT) && !(aClass instanceof PsiTypeParameter) 
467             && abstracts != null && unrelatedMethodContainingClass.isInterface()) {
468           if (defaultMethodContainingClass.isInheritor(unrelatedMethodContainingClass, true) && 
469               MethodSignatureUtil.isSubsignature(unrelatedMethod.getSignature(TypeConversionUtil.getSuperClassSubstitutor(unrelatedMethodContainingClass, defaultMethodContainingClass, PsiSubstitutor.EMPTY)), 
470                                                  defaultMethod.getSignature(PsiSubstitutor.EMPTY))) {
471             continue;
472           }
473           final String key = aClass instanceof PsiEnumConstantInitializer ? "enum.constant.should.implement.method" : "class.must.be.abstract";
474           final String message = JavaErrorMessages.message(key, HighlightUtil.formatClass(aClass, false), JavaHighlightUtil.formatMethod(abstracts.get(0)),
475                                                            HighlightUtil.formatClass(unrelatedMethodContainingClass, false));
476           final HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(classIdentifier).descriptionAndTooltip(message).create();
477           QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createImplementMethodsFix(aClass));
478           return info;
479         }
480         if (isInterface || abstracts == null || unrelatedMethodContainingClass.isInterface()) {
481           final List<PsiClass> defaultContainingClasses = ContainerUtil.mapNotNull(defaults, PsiMethod::getContainingClass);
482           final String unrelatedDefaults = hasUnrelatedDefaults(defaultContainingClasses);
483           if (unrelatedDefaults == null &&
484               (abstracts == null || !hasNotOverriddenAbstract(defaultContainingClasses, unrelatedMethodContainingClass))) {
485             continue;
486           }
487
488           final String message = unrelatedDefaults != null ? " inherits unrelated defaults for " : " inherits abstract and default for ";
489           final HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(classIdentifier).descriptionAndTooltip(
490             HighlightUtil.formatClass(aClass) +
491             message +
492             JavaHighlightUtil.formatMethod(defaultMethod) + " from types " +
493             (unrelatedDefaults != null ? unrelatedDefaults
494                                        : HighlightUtil.formatClass(defaultMethodContainingClass) + " and " + HighlightUtil.formatClass(unrelatedMethodContainingClass)))
495             .create();
496           QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createImplementMethodsFix(aClass));
497           return info;
498         }
499       }
500     }
501     return null;
502   }
503
504   private static boolean belongToOneHierarchy(@NotNull PsiClass defaultMethodContainingClass, @NotNull PsiClass unrelatedMethodContainingClass) {
505     return defaultMethodContainingClass.isInheritor(unrelatedMethodContainingClass, true) ||
506            unrelatedMethodContainingClass.isInheritor(defaultMethodContainingClass, true);
507   }
508
509   private static boolean hasNotOverriddenAbstract(List<PsiClass> defaultContainingClasses, @NotNull PsiClass abstractMethodContainingClass) {
510     return defaultContainingClasses.stream().noneMatch(containingClass -> belongToOneHierarchy(containingClass, abstractMethodContainingClass));
511   }
512
513   private static String hasUnrelatedDefaults(List<PsiClass> defaults) {
514     if (defaults.size() > 1) {
515       for (int i = 0; i < defaults.size(); i++) {
516         final PsiClass aClass1 = defaults.get(i);
517         for (int j = i + 1; j < defaults.size(); j++) {
518           final PsiClass aClass2 = defaults.get(j);
519           if (aClass2 != null && !belongToOneHierarchy(aClass1, aClass2)) {
520             return  HighlightUtil.formatClass(aClass1) + " and " + HighlightUtil.formatClass(aClass2);
521           }
522         }
523       }
524     }
525
526     return null;
527   }
528
529   static HighlightInfo checkUnrelatedConcrete(@NotNull PsiClass psiClass,
530                                               @NotNull PsiIdentifier classIdentifier) {
531     final PsiClass superClass = psiClass.getSuperClass();
532     if (superClass != null && superClass.hasTypeParameters()) {
533       final Collection<HierarchicalMethodSignature> visibleSignatures = superClass.getVisibleSignatures();
534       final Map<MethodSignature, PsiMethod> overrideEquivalent = new THashMap<>(MethodSignatureUtil.METHOD_PARAMETERS_ERASURE_EQUALITY);
535       for (HierarchicalMethodSignature hms : visibleSignatures) {
536         final PsiMethod method = hms.getMethod();
537         if (method.isConstructor()) continue;
538         if (method.hasModifierProperty(PsiModifier.ABSTRACT) || method.hasModifierProperty(PsiModifier.DEFAULT)) continue;
539         if (psiClass.findMethodsBySignature(method, false).length > 0) continue;
540         final PsiClass containingClass = method.getContainingClass();
541         if (containingClass == null) continue;
542         final PsiSubstitutor containingClassSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(containingClass, psiClass, PsiSubstitutor.EMPTY);
543         final PsiSubstitutor finalSubstitutor = PsiSuperMethodUtil
544           .obtainFinalSubstitutor(containingClass, containingClassSubstitutor, hms.getSubstitutor(), false);
545         final MethodSignatureBackedByPsiMethod signature = MethodSignatureBackedByPsiMethod.create(method, finalSubstitutor, false);
546         final PsiMethod foundMethod = overrideEquivalent.get(signature);
547         PsiClass foundMethodContainingClass;
548         if (foundMethod != null &&
549             !foundMethod.hasModifierProperty(PsiModifier.ABSTRACT) &&
550             !foundMethod.hasModifierProperty(PsiModifier.DEFAULT) &&
551             (foundMethodContainingClass = foundMethod.getContainingClass()) != null) {
552           final String description =
553             "Methods " +
554             JavaHighlightUtil.formatMethod(foundMethod) + " from " + HighlightUtil.formatClass(foundMethodContainingClass) +
555             " and " +
556             JavaHighlightUtil.formatMethod(method) + " from " + HighlightUtil.formatClass(containingClass) +
557             " are inherited with the same signature";
558           
559           final HighlightInfo info = HighlightInfo
560             .newHighlightInfo(HighlightInfoType.ERROR).range(classIdentifier).descriptionAndTooltip(
561               description)
562             .create();
563           //todo override fix
564           return info;
565         }
566         overrideEquivalent.put(signature, method);
567       }
568     }
569     return null;
570   }
571   
572   @Nullable
573   private static HighlightInfo checkSameErasureNotSubSignatureInner(@NotNull HierarchicalMethodSignature signature,
574                                                                     @NotNull PsiManager manager,
575                                                                     @NotNull PsiClass aClass,
576                                                                     @NotNull Map<MethodSignature, MethodSignatureBackedByPsiMethod> sameErasureMethods) {
577     PsiMethod method = signature.getMethod();
578     JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
579     if (!facade.getResolveHelper().isAccessible(method, aClass, null)) return null;
580     MethodSignature signatureToErase = method.getSignature(PsiSubstitutor.EMPTY);
581     MethodSignatureBackedByPsiMethod sameErasure = sameErasureMethods.get(signatureToErase);
582     HighlightInfo info;
583     if (sameErasure != null) {
584       if (aClass instanceof PsiTypeParameter ||
585           MethodSignatureUtil.findMethodBySuperMethod(aClass, sameErasure.getMethod(), false) != null ||
586           !(InheritanceUtil.isInheritorOrSelf(sameErasure.getMethod().getContainingClass(), method.getContainingClass(), true) ||
587             InheritanceUtil.isInheritorOrSelf(method.getContainingClass(), sameErasure.getMethod().getContainingClass(), true))) {
588         info = checkSameErasureNotSubSignatureOrSameClass(sameErasure, signature, aClass, method);
589         if (info != null) return info;
590       }
591     }
592     else {
593       sameErasureMethods.put(signatureToErase, signature);
594     }
595     List<HierarchicalMethodSignature> supers = signature.getSuperSignatures();
596     for (HierarchicalMethodSignature superSignature : supers) {
597       info = checkSameErasureNotSubSignatureInner(superSignature, manager, aClass, sameErasureMethods);
598       if (info != null) return info;
599
600       if (superSignature.isRaw() && !signature.isRaw()) {
601         final PsiType[] parameterTypes = signature.getParameterTypes();
602         PsiType[] erasedTypes = superSignature.getErasedParameterTypes();
603         for (int i = 0; i < erasedTypes.length; i++) {
604           if (!Comparing.equal(parameterTypes[i], erasedTypes[i])) {
605             return getSameErasureMessage(false, method, superSignature.getMethod(),
606                                          HighlightNamesUtil.getClassDeclarationTextRange(aClass));
607           }
608         }
609       }
610
611     }
612     return null;
613   }
614
615   @Nullable
616   private static HighlightInfo checkSameErasureNotSubSignatureOrSameClass(final MethodSignatureBackedByPsiMethod signatureToCheck,
617                                                                           final HierarchicalMethodSignature superSignature,
618                                                                           final PsiClass aClass,
619                                                                           final PsiMethod superMethod) {
620     final PsiMethod checkMethod = signatureToCheck.getMethod();
621     if (superMethod.equals(checkMethod)) return null;
622     PsiClass checkContainingClass = checkMethod.getContainingClass();
623     LOG.assertTrue(checkContainingClass != null);
624     PsiClass superContainingClass = superMethod.getContainingClass();
625     boolean checkEqualsSuper = checkContainingClass.equals(superContainingClass);
626     if (checkMethod.isConstructor()) {
627       if (!superMethod.isConstructor() || !checkEqualsSuper) return null;
628     }
629     else if (superMethod.isConstructor()) return null;
630
631     final boolean atLeast17 = JavaVersionService.getInstance().isAtLeast(aClass, JavaSdkVersion.JDK_1_7);
632     if (checkMethod.hasModifierProperty(PsiModifier.STATIC) && !checkEqualsSuper && !atLeast17) {
633       return null;
634     }
635
636     if (superMethod.hasModifierProperty(PsiModifier.STATIC) && superContainingClass != null &&
637         superContainingClass.isInterface() && PsiUtil.isLanguageLevel8OrHigher(superContainingClass)) {
638       return null;
639     }
640
641     final PsiType retErasure1 = TypeConversionUtil.erasure(checkMethod.getReturnType());
642     final PsiType retErasure2 = TypeConversionUtil.erasure(superMethod.getReturnType());
643
644     boolean differentReturnTypeErasure = !Comparing.equal(retErasure1, retErasure2);
645     if (checkEqualsSuper && atLeast17) {
646       if (retErasure1 != null && retErasure2 != null) {
647         differentReturnTypeErasure = !TypeConversionUtil.isAssignable(retErasure1, retErasure2);
648       }
649       else {
650         differentReturnTypeErasure = !(retErasure1 == null && retErasure2 == null);
651       }
652     }
653
654     if (differentReturnTypeErasure &&
655         !TypeConversionUtil.isVoidType(retErasure1) &&
656         !TypeConversionUtil.isVoidType(retErasure2) &&
657         !(checkEqualsSuper && Arrays.equals(superSignature.getParameterTypes(), signatureToCheck.getParameterTypes())) &&
658         !atLeast17) {
659       int idx = 0;
660       final PsiType[] erasedTypes = signatureToCheck.getErasedParameterTypes();
661       boolean erasure = erasedTypes.length > 0;
662       for (PsiType type : superSignature.getParameterTypes()) {
663         erasure &= Comparing.equal(type, erasedTypes[idx]);
664         idx++;
665       }
666
667       if (!erasure) return null;
668     }
669
670     if (!checkEqualsSuper && MethodSignatureUtil.isSubsignature(superSignature, signatureToCheck)) {
671       return null;
672     }
673     if (superContainingClass != null && !superContainingClass.isInterface() && checkContainingClass.isInterface() && !aClass.equals(superContainingClass)) return null;
674     if (aClass.equals(checkContainingClass)) {
675       boolean sameClass = aClass.equals(superContainingClass);
676       return getSameErasureMessage(sameClass, checkMethod, superMethod, HighlightNamesUtil.getMethodDeclarationTextRange(checkMethod));
677     }
678     else {
679       return getSameErasureMessage(false, checkMethod, superMethod, HighlightNamesUtil.getClassDeclarationTextRange(aClass));
680     }
681   }
682
683   private static HighlightInfo getSameErasureMessage(final boolean sameClass, @NotNull PsiMethod method, @NotNull PsiMethod superMethod,
684                                                      TextRange textRange) {
685      @NonNls final String key = sameClass ? "generics.methods.have.same.erasure" :
686                                method.hasModifierProperty(PsiModifier.STATIC) ?
687                                "generics.methods.have.same.erasure.hide" :
688                                "generics.methods.have.same.erasure.override";
689     String description = JavaErrorMessages.message(key, HighlightMethodUtil.createClashMethodMessage(method, superMethod, !sameClass));
690     return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
691   }
692
693   static HighlightInfo checkTypeParameterInstantiation(PsiNewExpression expression) {
694     PsiJavaCodeReferenceElement classReference = expression.getClassOrAnonymousClassReference();
695     if (classReference == null) return null;
696     final JavaResolveResult result = classReference.advancedResolve(false);
697     final PsiElement element = result.getElement();
698     if (element instanceof PsiTypeParameter) {
699       String description = JavaErrorMessages.message("generics.type.parameter.cannot.be.instantiated",
700                                                      HighlightUtil.formatClass((PsiTypeParameter)element));
701       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(classReference).descriptionAndTooltip(description).create();
702     }
703     return null;
704   }
705
706   static HighlightInfo checkWildcardUsage(PsiTypeElement typeElement) {
707     PsiType type = typeElement.getType();
708     if (type instanceof PsiWildcardType) {
709       if (typeElement.getParent() instanceof PsiReferenceParameterList) {
710         PsiElement parent = typeElement.getParent().getParent();
711         LOG.assertTrue(parent instanceof PsiJavaCodeReferenceElement, parent);
712         PsiElement refParent = parent.getParent();
713         if (refParent instanceof PsiAnonymousClass) refParent = refParent.getParent();
714         if (refParent instanceof PsiNewExpression) {
715           PsiNewExpression newExpression = (PsiNewExpression)refParent;
716           if (!(newExpression.getType() instanceof PsiArrayType)) {
717             String description = JavaErrorMessages.message("wildcard.type.cannot.be.instantiated", JavaHighlightUtil.formatType(type));
718             return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(description).create();
719           }
720         }
721         else if (refParent instanceof PsiReferenceList) {
722           PsiElement refPParent = refParent.getParent();
723           if (!(refPParent instanceof PsiTypeParameter) || refParent != ((PsiTypeParameter)refPParent).getExtendsList()) {
724             String description = JavaErrorMessages.message("generics.wildcard.not.expected");
725             return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(description).create();
726           }
727         }
728       }
729       else {
730         String description = JavaErrorMessages.message("generics.wildcards.may.be.used.only.as.reference.parameters");
731         return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(description).create();
732       }
733     }
734
735     return null;
736   }
737
738   static HighlightInfo checkReferenceTypeUsedAsTypeArgument(PsiTypeElement typeElement, LanguageLevel level) {
739     final PsiType type = typeElement.getType();
740     if (type != PsiType.NULL && type instanceof PsiPrimitiveType ||
741         type instanceof PsiWildcardType && ((PsiWildcardType)type).getBound() instanceof PsiPrimitiveType) {
742       final PsiElement element = new PsiMatcherImpl(typeElement)
743         .parent(PsiMatchers.hasClass(PsiReferenceParameterList.class))
744         .parent(PsiMatchers.hasClass(PsiJavaCodeReferenceElement.class, PsiNewExpression.class))
745         .getElement();
746       if (element == null) return null;
747
748       if (level.isAtLeast(LanguageLevel.JDK_X)) return null;
749
750       String text = JavaErrorMessages.message("generics.type.argument.cannot.be.of.primitive.type");
751       HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(text).create();
752
753       PsiType toConvert = type;
754       if (type instanceof PsiWildcardType) {
755         toConvert = ((PsiWildcardType)type).getBound();
756       }
757       if (toConvert instanceof PsiPrimitiveType) {
758         final PsiClassType boxedType = ((PsiPrimitiveType)toConvert).getBoxedType(typeElement);
759         if (boxedType != null) {
760           QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createReplacePrimitiveWithBoxedTypeAction(
761             typeElement, toConvert.getPresentableText(), ((PsiPrimitiveType)toConvert).getBoxedTypeName()));
762         }
763       }
764       return highlightInfo;
765     }
766
767     return null;
768   }
769
770   static HighlightInfo checkForeachExpressionTypeIsIterable(PsiExpression expression) {
771     if (expression == null || expression.getType() == null) return null;
772     final PsiType itemType = JavaGenericsUtil.getCollectionItemType(expression);
773     if (itemType == null) {
774       String description = JavaErrorMessages.message("foreach.not.applicable",
775                                                      JavaHighlightUtil.formatType(expression.getType()));
776       final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(description).create();
777       QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createNotIterableForEachLoopFix(expression));
778       return highlightInfo;
779     }
780     return null;
781   }
782
783   static HighlightInfo checkForEachParameterType(@NotNull PsiForeachStatement statement, @NotNull PsiParameter parameter) {
784     final PsiExpression expression = statement.getIteratedValue();
785     final PsiType itemType = expression == null ? null : JavaGenericsUtil.getCollectionItemType(expression);
786     if (itemType == null) return null;
787
788     final PsiType parameterType = parameter.getType();
789     if (TypeConversionUtil.isAssignable(parameterType, itemType)) {
790       return null;
791     }
792     HighlightInfo highlightInfo = HighlightUtil.createIncompatibleTypeHighlightInfo(itemType, parameterType, parameter.getTextRange(), 0);
793     HighlightUtil.registerChangeVariableTypeFixes(parameter, itemType, expression, highlightInfo);
794     return highlightInfo;
795   }
796
797   //http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.9.2
798   @Nullable
799   static HighlightInfo checkAccessStaticFieldFromEnumConstructor(@NotNull PsiReferenceExpression expr, @NotNull JavaResolveResult result) {
800     final PsiElement resolved = result.getElement();
801
802     if (!(resolved instanceof PsiField)) return null;
803     if (!((PsiModifierListOwner)resolved).hasModifierProperty(PsiModifier.STATIC)) return null;
804     if (expr.getParent() instanceof PsiSwitchLabelStatement) return null;
805     final PsiMember constructorOrInitializer = PsiUtil.findEnclosingConstructorOrInitializer(expr);
806     if (constructorOrInitializer == null) return null;
807     if (constructorOrInitializer.hasModifierProperty(PsiModifier.STATIC)) return null;
808     final PsiClass aClass = constructorOrInitializer instanceof PsiEnumConstantInitializer ? 
809                             (PsiClass)constructorOrInitializer : constructorOrInitializer.getContainingClass();
810     if (aClass == null || !(aClass.isEnum() || aClass instanceof PsiEnumConstantInitializer)) return null;
811     final PsiField field = (PsiField)resolved;
812     if (aClass instanceof PsiEnumConstantInitializer) {
813       if (field.getContainingClass() != aClass.getSuperClass()) return null;
814     } else if (field.getContainingClass() != aClass) return null;
815
816
817     if (!JavaVersionService.getInstance().isAtLeast(field, JavaSdkVersion.JDK_1_6)) {
818       final PsiType type = field.getType();
819       if (type instanceof PsiClassType && ((PsiClassType)type).resolve() == aClass) return null;
820     }
821
822     if (PsiUtil.isCompileTimeConstant((PsiVariable)field)) return null;
823
824     String description = JavaErrorMessages.message(
825       "illegal.to.access.static.member.from.enum.constructor.or.instance.initializer",
826       HighlightMessageUtil.getSymbolName(resolved, result.getSubstitutor())
827     );
828
829     return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expr).descriptionAndTooltip(description).create();
830   }
831
832   @Nullable
833   static HighlightInfo checkEnumInstantiation(PsiElement expression, PsiClass aClass) {
834     if (aClass != null && aClass.isEnum() && 
835         (!(expression instanceof PsiNewExpression) ||
836          ((PsiNewExpression)expression).getArrayDimensions().length == 0 && ((PsiNewExpression)expression).getArrayInitializer() == null)) {
837       String description = JavaErrorMessages.message("enum.types.cannot.be.instantiated");
838       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(description).create();
839     }
840     return null;
841   }
842
843   @Nullable
844   static HighlightInfo checkGenericArrayCreation(PsiElement element, PsiType type) {
845     if (type instanceof PsiArrayType) {
846       if (!JavaGenericsUtil.isReifiableType(((PsiArrayType)type).getComponentType())) {
847         String description = JavaErrorMessages.message("generic.array.creation");
848         return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description).create();
849       }
850     }
851
852     return null;
853   }
854
855   private static final MethodSignature ourValuesEnumSyntheticMethod = MethodSignatureUtil.createMethodSignature("values",
856                                                                                                                 PsiType.EMPTY_ARRAY,
857                                                                                                                 PsiTypeParameter.EMPTY_ARRAY,
858                                                                                                                 PsiSubstitutor.EMPTY);
859
860   static boolean isEnumSyntheticMethod(MethodSignature methodSignature, Project project) {
861     if (methodSignature.equals(ourValuesEnumSyntheticMethod)) return true;
862     final PsiType javaLangString = PsiType.getJavaLangString(PsiManager.getInstance(project), GlobalSearchScope.allScope(project));
863     final MethodSignature valueOfMethod = MethodSignatureUtil.createMethodSignature("valueOf", new PsiType[]{javaLangString}, PsiTypeParameter.EMPTY_ARRAY,
864                                                                                     PsiSubstitutor.EMPTY);
865     return valueOfMethod.equals(methodSignature);
866   }
867
868   @Nullable
869   static HighlightInfo checkTypeParametersList(PsiTypeParameterList list, PsiTypeParameter[] parameters, @NotNull LanguageLevel level) {
870     final PsiElement parent = list.getParent();
871     if (parent instanceof PsiClass && ((PsiClass)parent).isEnum()) {
872       String description = JavaErrorMessages.message("generics.enum.may.not.have.type.parameters");
873       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(list).descriptionAndTooltip(description).create();
874     }
875     if (PsiUtil.isAnnotationMethod(parent)) {
876       String description = JavaErrorMessages.message("generics.annotation.members.may.not.have.type.parameters");
877       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(list).descriptionAndTooltip(description).create();
878     }
879     if (parent instanceof PsiClass && ((PsiClass)parent).isAnnotationType()) {
880       String description = JavaErrorMessages.message("annotation.may.not.have.type.parameters");
881       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(list).descriptionAndTooltip(description).create();
882     }
883
884     for (int i = 0; i < parameters.length; i++) {
885       final PsiTypeParameter typeParameter1 = parameters[i];
886       final HighlightInfo cyclicInheritance = HighlightClassUtil.checkCyclicInheritance(typeParameter1);
887       if (cyclicInheritance != null) return cyclicInheritance;
888       String name1 = typeParameter1.getName();
889       for (int j = i + 1; j < parameters.length; j++) {
890         final PsiTypeParameter typeParameter2 = parameters[j];
891         String name2 = typeParameter2.getName();
892         if (Comparing.strEqual(name1, name2)) {
893           String message = JavaErrorMessages.message("generics.duplicate.type.parameter", name1);
894           return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeParameter2).descriptionAndTooltip(message).create();
895         }
896       }
897       if (!level.isAtLeast(LanguageLevel.JDK_1_7)) {
898         for (PsiJavaCodeReferenceElement referenceElement : typeParameter1.getExtendsList().getReferenceElements()) {
899           final PsiElement resolve = referenceElement.resolve();
900           if (resolve instanceof PsiTypeParameter && ArrayUtilRt.find(parameters, resolve) > i) {
901             return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(referenceElement.getTextRange()).descriptionAndTooltip("Illegal forward reference").create();
902           }
903         }
904       }
905     }
906     return null;
907   }
908
909   @Nullable
910   static Collection<HighlightInfo> checkCatchParameterIsClass(PsiParameter parameter) {
911     if (!(parameter.getDeclarationScope() instanceof PsiCatchSection)) return null;
912     final Collection<HighlightInfo> result = ContainerUtil.newArrayList();
913
914     final List<PsiTypeElement> typeElements = PsiUtil.getParameterTypeElements(parameter);
915     for (PsiTypeElement typeElement : typeElements) {
916       final PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(typeElement.getType());
917       if (aClass instanceof PsiTypeParameter) {
918         final String message = JavaErrorMessages.message("generics.cannot.catch.type.parameters");
919         result.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(message).create());
920       }
921     }
922
923     return result;
924   }
925
926   static HighlightInfo checkInstanceOfGenericType(PsiInstanceOfExpression expression) {
927     final PsiTypeElement checkTypeElement = expression.getCheckType();
928     if (checkTypeElement == null) return null;
929     return isIllegalForInstanceOf(checkTypeElement.getType(), checkTypeElement);
930   }
931
932   /**
933    * 15.20.2 Type Comparison Operator instanceof
934    * ReferenceType mentioned after the instanceof operator is reifiable
935    */
936   private static HighlightInfo isIllegalForInstanceOf(PsiType type, final PsiTypeElement typeElement) {
937     final PsiClass resolved = PsiUtil.resolveClassInClassTypeOnly(type);
938     if (resolved instanceof PsiTypeParameter) {
939       String description = JavaErrorMessages.message("generics.cannot.instanceof.type.parameters");
940       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(description).create();
941     }
942
943     if (!JavaGenericsUtil.isReifiableType(type)) {
944       String description = JavaErrorMessages.message("illegal.generic.type.for.instanceof");
945       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(typeElement).descriptionAndTooltip(description).create();
946     }
947
948     return null;
949   }
950
951   static HighlightInfo checkClassObjectAccessExpression(PsiClassObjectAccessExpression expression) {
952     PsiType type = expression.getOperand().getType();
953     if (type instanceof PsiClassType) {
954       return canSelectFrom((PsiClassType)type, expression.getOperand());
955     }
956     if (type instanceof PsiArrayType) {
957       final PsiType arrayComponentType = type.getDeepComponentType();
958       if (arrayComponentType instanceof PsiClassType) {
959         return canSelectFrom((PsiClassType)arrayComponentType, expression.getOperand());
960       }
961     }
962
963     return null;
964   }
965
966   @Nullable
967   private static HighlightInfo canSelectFrom(PsiClassType type, PsiTypeElement operand) {
968     PsiClass aClass = type.resolve();
969     if (aClass instanceof PsiTypeParameter) {
970       String description = JavaErrorMessages.message("cannot.select.dot.class.from.type.variable");
971       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(operand).descriptionAndTooltip(description).create();
972     }
973     if (type.getParameters().length > 0) {
974       return HighlightInfo
975         .newHighlightInfo(HighlightInfoType.ERROR).range(operand).descriptionAndTooltip("Cannot select from parameterized type").create();
976     }
977     return null;
978   }
979
980   @Nullable
981   static HighlightInfo checkOverrideAnnotation(@NotNull PsiMethod method,
982                                                @NotNull PsiAnnotation overrideAnnotation,
983                                                @NotNull LanguageLevel languageLevel) {
984     try {
985       MethodSignatureBackedByPsiMethod superMethod = SuperMethodsSearch.search(method, null, true, false).findFirst();
986       if (superMethod != null && method.getContainingClass().isInterface()) {
987         final PsiMethod psiMethod = superMethod.getMethod();
988         final PsiClass containingClass = psiMethod.getContainingClass();
989         if (containingClass != null &&
990             CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName()) &&
991             psiMethod.hasModifierProperty(PsiModifier.PROTECTED)) {
992           superMethod = null;
993         }
994       }
995       if (superMethod == null) {
996         String description = JavaErrorMessages.message("method.does.not.override.super");
997         HighlightInfo highlightInfo =
998           HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(overrideAnnotation).descriptionAndTooltip(description).create();
999         QUICK_FIX_FACTORY.registerPullAsAbstractUpFixes(method, new QuickFixActionRegistrarImpl(highlightInfo));
1000         return highlightInfo;
1001       }
1002       PsiClass superClass = superMethod.getMethod().getContainingClass();
1003       if (languageLevel.equals(LanguageLevel.JDK_1_5) &&
1004           superClass != null &&
1005           superClass.isInterface()) {
1006         String description = JavaErrorMessages.message("override.not.allowed.in.interfaces");
1007         HighlightInfo info =
1008           HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(overrideAnnotation).descriptionAndTooltip(description).create();
1009         QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createIncreaseLanguageLevelFix(LanguageLevel.JDK_1_6));
1010         return info;
1011       }
1012       return null;
1013     }
1014     catch (IndexNotReadyException e) {
1015       return null;
1016     }
1017   }
1018
1019   @Nullable
1020   static HighlightInfo checkSafeVarargsAnnotation(PsiMethod method, LanguageLevel languageLevel) {
1021     PsiModifierList list = method.getModifierList();
1022     final PsiAnnotation safeVarargsAnnotation = list.findAnnotation("java.lang.SafeVarargs");
1023     if (safeVarargsAnnotation == null) {
1024       return null;
1025     }
1026     try {
1027       if (!method.isVarArgs()) {
1028         return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(safeVarargsAnnotation).descriptionAndTooltip(
1029           "@SafeVarargs is not allowed on methods with fixed arity").create();
1030       }
1031       if (!isSafeVarargsNoOverridingCondition(method, languageLevel)) {
1032         return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(safeVarargsAnnotation).descriptionAndTooltip(
1033           "@SafeVarargs is not allowed on non-final instance methods").create();
1034       }
1035
1036       final PsiParameter varParameter = method.getParameterList().getParameters()[method.getParameterList().getParametersCount() - 1];
1037
1038       for (PsiReference reference : ReferencesSearch.search(varParameter)) {
1039         final PsiElement element = reference.getElement();
1040         if (element instanceof PsiExpression && !PsiUtil.isAccessedForReading((PsiExpression)element)) {
1041           return HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(element).descriptionAndTooltip(
1042             "@SafeVarargs do not suppress potentially unsafe operations").create();
1043         }
1044       }
1045
1046
1047       LOG.assertTrue(varParameter.isVarArgs());
1048       final PsiEllipsisType ellipsisType = (PsiEllipsisType)varParameter.getType();
1049       final PsiType componentType = ellipsisType.getComponentType();
1050       if (JavaGenericsUtil.isReifiableType(componentType)) {
1051         PsiElement element = varParameter.getTypeElement();
1052         return HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(element).descriptionAndTooltip(
1053           "@SafeVarargs is not applicable for reifiable types").create();
1054       }
1055       return null;
1056     }
1057     catch (IndexNotReadyException e) {
1058       return null;
1059     }
1060   }
1061
1062   public static boolean isSafeVarargsNoOverridingCondition(PsiMethod method, LanguageLevel languageLevel) {
1063     return method.hasModifierProperty(PsiModifier.FINAL) ||
1064            method.hasModifierProperty(PsiModifier.STATIC) ||
1065            method.isConstructor() ||
1066            method.hasModifierProperty(PsiModifier.PRIVATE) && languageLevel.isAtLeast(LanguageLevel.JDK_1_9);
1067   }
1068
1069   static void checkEnumConstantForConstructorProblems(@NotNull PsiEnumConstant enumConstant,
1070                                                       @NotNull HighlightInfoHolder holder,
1071                                                       @NotNull JavaSdkVersion javaSdkVersion) {
1072     PsiClass containingClass = enumConstant.getContainingClass();
1073     if (enumConstant.getInitializingClass() == null) {
1074       HighlightInfo highlightInfo = HighlightClassUtil.checkInstantiationOfAbstractClass(containingClass, enumConstant.getNameIdentifier());
1075       if (highlightInfo != null) {
1076         QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createImplementMethodsFix(enumConstant));
1077         holder.add(highlightInfo);
1078         return;
1079       }
1080       highlightInfo = HighlightClassUtil.checkClassWithAbstractMethods(enumConstant.getContainingClass(), enumConstant, enumConstant.getNameIdentifier().getTextRange());
1081       if (highlightInfo != null) {
1082         holder.add(highlightInfo);
1083         return;
1084       }
1085     }
1086     PsiClassType type = JavaPsiFacade.getInstance(holder.getProject()).getElementFactory().createType(containingClass);
1087
1088     HighlightMethodUtil.checkConstructorCall(type.resolveGenerics(), enumConstant, type, null, holder, javaSdkVersion);
1089   }
1090
1091   @Nullable
1092   static HighlightInfo checkEnumSuperConstructorCall(PsiMethodCallExpression expr) {
1093     PsiReferenceExpression methodExpression = expr.getMethodExpression();
1094     final PsiElement refNameElement = methodExpression.getReferenceNameElement();
1095     if (refNameElement != null && PsiKeyword.SUPER.equals(refNameElement.getText())) {
1096       final PsiMember constructor = PsiUtil.findEnclosingConstructorOrInitializer(expr);
1097       if (constructor instanceof PsiMethod) {
1098         final PsiClass aClass = constructor.getContainingClass();
1099         if (aClass != null && aClass.isEnum()) {
1100           final String message = JavaErrorMessages.message("call.to.super.is.not.allowed.in.enum.constructor");
1101           return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expr).descriptionAndTooltip(message).create();
1102         }
1103       }
1104     }
1105     return null;
1106   }
1107
1108   @Nullable
1109   static HighlightInfo checkVarArgParameterIsLast(@NotNull PsiParameter parameter) {
1110     PsiElement declarationScope = parameter.getDeclarationScope();
1111     if (declarationScope instanceof PsiMethod) {
1112       PsiParameter[] params = ((PsiMethod)declarationScope).getParameterList().getParameters();
1113       if (params[params.length - 1] != parameter) {
1114         String description = JavaErrorMessages.message("vararg.not.last.parameter");
1115         HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(parameter).descriptionAndTooltip(description).create();
1116         QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createMakeVarargParameterLastFix(parameter));
1117         return info;
1118       }
1119     }
1120     return null;
1121   }
1122
1123   @Nullable
1124   static List<HighlightInfo> checkEnumConstantModifierList(PsiModifierList modifierList) {
1125     List<HighlightInfo> list = null;
1126     PsiElement[] children = modifierList.getChildren();
1127     for (PsiElement child : children) {
1128       if (child instanceof PsiKeyword) {
1129         if (list == null) {
1130           list = new ArrayList<>();
1131         }
1132         String description = JavaErrorMessages.message("modifiers.for.enum.constants");
1133         list.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(child).descriptionAndTooltip(description).create());
1134       }
1135     }
1136     return list;
1137   }
1138
1139   @Nullable
1140   static HighlightInfo checkParametersAllowed(PsiReferenceParameterList refParamList) {
1141     final PsiElement parent = refParamList.getParent();
1142     if (parent instanceof PsiReferenceExpression) {
1143       final PsiElement grandParent = parent.getParent();
1144       if (!(grandParent instanceof PsiMethodCallExpression) && !(parent instanceof PsiMethodReferenceExpression)) {
1145         final String message = JavaErrorMessages.message("generics.reference.parameters.not.allowed");
1146         return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refParamList).descriptionAndTooltip(message).create();
1147       }
1148     }
1149
1150     return null;
1151   }
1152
1153   @Nullable
1154   static HighlightInfo checkParametersOnRaw(PsiReferenceParameterList refParamList) {
1155     JavaResolveResult resolveResult = null;
1156     PsiElement parent = refParamList.getParent();
1157     PsiElement qualifier = null;
1158     if (parent instanceof PsiJavaCodeReferenceElement) {
1159       resolveResult = ((PsiJavaCodeReferenceElement)parent).advancedResolve(false);
1160       qualifier = ((PsiJavaCodeReferenceElement)parent).getQualifier();
1161     }
1162     else if (parent instanceof PsiCallExpression) {
1163       resolveResult = ((PsiCallExpression)parent).resolveMethodGenerics();
1164       if (parent instanceof PsiMethodCallExpression) {
1165         final PsiReferenceExpression methodExpression = ((PsiMethodCallExpression)parent).getMethodExpression();
1166         qualifier = methodExpression.getQualifier();
1167       }
1168     }
1169     if (resolveResult != null) {
1170       PsiElement element = resolveResult.getElement();
1171       if (!(element instanceof PsiTypeParameterListOwner)) return null;
1172       if (((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.STATIC)) return null;
1173       if (qualifier instanceof PsiJavaCodeReferenceElement && ((PsiJavaCodeReferenceElement)qualifier).resolve() instanceof PsiTypeParameter) return null;
1174       PsiClass containingClass = ((PsiMember)element).getContainingClass();
1175       if (containingClass != null && PsiUtil.isRawSubstitutor(containingClass, resolveResult.getSubstitutor())) {
1176         if ((parent instanceof PsiCallExpression || parent instanceof PsiMethodReferenceExpression) && PsiUtil.isLanguageLevel7OrHigher(parent)) {
1177           return null;
1178         }
1179
1180         if (element instanceof PsiMethod) {
1181           if (((PsiMethod)element).findSuperMethods().length > 0) return null;
1182           if (qualifier instanceof PsiReferenceExpression){
1183             final PsiType type = ((PsiReferenceExpression)qualifier).getType();
1184             final boolean isJavac7 = JavaVersionService.getInstance().isAtLeast(containingClass, JavaSdkVersion.JDK_1_7);
1185             if (type instanceof PsiClassType && isJavac7 && ((PsiClassType)type).isRaw()) return null;
1186             final PsiClass typeParameter = PsiUtil.resolveClassInType(type);
1187             if (typeParameter instanceof PsiTypeParameter) {
1188               if (isJavac7) return null;
1189               for (PsiClassType classType : typeParameter.getExtendsListTypes()) {
1190                 final PsiClass resolve = classType.resolve();
1191                 if (resolve != null) {
1192                   final PsiMethod[] superMethods = resolve.findMethodsBySignature((PsiMethod)element, true);
1193                   for (PsiMethod superMethod : superMethods) {
1194                     if (!PsiUtil.isRawSubstitutor(superMethod, resolveResult.getSubstitutor())) {
1195                       return null;
1196                     }
1197                   }
1198                 }
1199               }
1200             }
1201           }
1202         }
1203         final String message = element instanceof PsiClass
1204                                ? JavaErrorMessages.message("generics.type.arguments.on.raw.type")
1205                                : JavaErrorMessages.message("generics.type.arguments.on.raw.method");
1206
1207         return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refParamList).descriptionAndTooltip(message).create();
1208       }
1209     }
1210     return null;
1211   }
1212
1213   static HighlightInfo checkCannotInheritFromEnum(PsiClass superClass, PsiElement elementToHighlight) {
1214     HighlightInfo errorResult = null;
1215     if (Comparing.strEqual("java.lang.Enum", superClass.getQualifiedName())) {
1216       String message = JavaErrorMessages.message("classes.extends.enum");
1217       errorResult =
1218         HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(elementToHighlight).descriptionAndTooltip(message).create();
1219     }
1220     return errorResult;
1221   }
1222
1223   static HighlightInfo checkGenericCannotExtendException(PsiReferenceList list) {
1224     PsiElement parent = list.getParent();
1225     if (!(parent instanceof PsiClass)) return null;
1226     PsiClass aClass = (PsiClass)parent;
1227
1228     if (!aClass.hasTypeParameters() || aClass.getExtendsList() != list) return null;
1229     PsiJavaCodeReferenceElement[] referenceElements = list.getReferenceElements();
1230     PsiClass throwableClass = null;
1231     for (PsiJavaCodeReferenceElement referenceElement : referenceElements) {
1232       PsiElement resolved = referenceElement.resolve();
1233       if (!(resolved instanceof PsiClass)) continue;
1234       if (throwableClass == null) {
1235         throwableClass = JavaPsiFacade.getInstance(aClass.getProject()).findClass("java.lang.Throwable", aClass.getResolveScope());
1236       }
1237       if (InheritanceUtil.isInheritorOrSelf((PsiClass)resolved, throwableClass, true)) {
1238         String message = JavaErrorMessages.message("generic.extend.exception");
1239         HighlightInfo highlightInfo =
1240           HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(referenceElement).descriptionAndTooltip(message).create();
1241         PsiClassType classType = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory().createType((PsiClass)resolved);
1242         QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createExtendsListFix(aClass, classType, false));
1243         return highlightInfo;
1244       }
1245     }
1246     return null;
1247   }
1248
1249   static HighlightInfo checkEnumMustNotBeLocal(final PsiClass aClass) {
1250     if (!aClass.isEnum()) return null;
1251     PsiElement parent = aClass.getParent();
1252     if (!(parent instanceof PsiClass || parent instanceof PsiFile || parent instanceof PsiClassLevelDeclarationStatement)) {
1253       String description = JavaErrorMessages.message("local.enum");
1254       TextRange textRange = HighlightNamesUtil.getClassDeclarationTextRange(aClass);
1255       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
1256     }
1257     return null;
1258   }
1259
1260   static HighlightInfo checkEnumWithoutConstantsCantHaveAbstractMethods(final PsiClass aClass) {
1261     if (!aClass.isEnum()) return null;
1262     for (PsiField field : aClass.getFields()) {
1263       if (field instanceof PsiEnumConstant) {
1264         return null;
1265       }
1266     }
1267     for (PsiMethod method : aClass.getMethods()) {
1268       if (method.hasModifierProperty(PsiModifier.ABSTRACT)) {
1269         final String description = "Enum declaration without enum constants cannot have abstract methods";
1270         final TextRange textRange = HighlightNamesUtil.getClassDeclarationTextRange(aClass);
1271         return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(textRange).descriptionAndTooltip(description).create();
1272       }
1273     }
1274     return null;
1275   }
1276
1277   static HighlightInfo checkSelectStaticClassFromParameterizedType(final PsiElement resolved, final PsiJavaCodeReferenceElement ref) {
1278     if (resolved instanceof PsiClass && ((PsiClass)resolved).hasModifierProperty(PsiModifier.STATIC)) {
1279       final PsiElement qualifier = ref.getQualifier();
1280       if (qualifier instanceof PsiJavaCodeReferenceElement) {
1281         final PsiReferenceParameterList parameterList = ((PsiJavaCodeReferenceElement)qualifier).getParameterList();
1282         if (parameterList != null && parameterList.getTypeArguments().length > 0) {
1283           final String message = JavaErrorMessages.message("generics.select.static.class.from.parameterized.type",
1284                                                            HighlightUtil.formatClass((PsiClass)resolved));
1285           return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(parameterList).descriptionAndTooltip(message).create();
1286         }
1287       }
1288     }
1289     return null;
1290   }
1291
1292   static HighlightInfo checkCannotInheritFromTypeParameter(final PsiClass superClass, final PsiJavaCodeReferenceElement toHighlight) {
1293     if (superClass instanceof PsiTypeParameter) {
1294       String description = JavaErrorMessages.message("class.cannot.inherit.from.its.type.parameter");
1295       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(toHighlight).descriptionAndTooltip(description).create();
1296     }
1297     return null;
1298   }
1299
1300   /**
1301    * http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.8
1302    */
1303   static HighlightInfo checkRawOnParameterizedType(@NotNull PsiJavaCodeReferenceElement parent, PsiElement resolved) {
1304     PsiReferenceParameterList list = parent.getParameterList();
1305     if (list == null || list.getTypeArguments().length > 0) return null;
1306     final PsiElement qualifier = parent.getQualifier();
1307     if (qualifier instanceof PsiJavaCodeReferenceElement &&
1308         ((PsiJavaCodeReferenceElement)qualifier).getTypeParameters().length > 0 &&
1309         resolved instanceof PsiTypeParameterListOwner &&
1310         ((PsiTypeParameterListOwner)resolved).hasTypeParameters() &&
1311         !((PsiTypeParameterListOwner)resolved).hasModifierProperty(PsiModifier.STATIC)) {
1312       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(parent).descriptionAndTooltip(
1313         "Improper formed type; some type parameters are missing").create();
1314     }
1315     return null;
1316   }
1317
1318   static HighlightInfo checkCannotPassInner(PsiJavaCodeReferenceElement ref) {
1319     if (ref.getParent() instanceof PsiTypeElement) {
1320       final PsiClass psiClass = PsiTreeUtil.getParentOfType(ref, PsiClass.class);
1321       if (psiClass == null) return null;
1322       if (PsiTreeUtil.isAncestor(psiClass.getExtendsList(), ref, false) ||
1323           PsiTreeUtil.isAncestor(psiClass.getImplementsList(), ref, false)) {
1324         final PsiElement qualifier = ref.getQualifier();
1325         if (qualifier instanceof PsiJavaCodeReferenceElement && ((PsiJavaCodeReferenceElement)qualifier).resolve() == psiClass) {
1326           final PsiJavaCodeReferenceElement referenceElement = PsiTreeUtil.getParentOfType(ref, PsiJavaCodeReferenceElement.class);
1327           if (referenceElement == null) return null;
1328           final PsiElement typeClass = referenceElement.resolve();
1329           if (!(typeClass instanceof PsiClass)) return null;
1330           final PsiElement resolve = ref.resolve();
1331           final PsiClass containingClass = resolve != null ? ((PsiClass)resolve).getContainingClass() : null;
1332           if (containingClass == null) return null;
1333           if (psiClass.isInheritor(containingClass, true) ||
1334               unqualifiedNestedClassReferenceAccessedViaContainingClassInheritance((PsiClass)typeClass, ((PsiClass)resolve).getExtendsList()) ||
1335               unqualifiedNestedClassReferenceAccessedViaContainingClassInheritance((PsiClass)typeClass, ((PsiClass)resolve).getImplementsList())) {
1336             return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).descriptionAndTooltip(((PsiClass)resolve).getName() + " is not accessible in current context").range(ref).create();
1337           }
1338         }
1339       }
1340     }
1341     return null;
1342   }
1343
1344   private static boolean unqualifiedNestedClassReferenceAccessedViaContainingClassInheritance(PsiClass containingClass,
1345                                                                                               PsiReferenceList referenceList) {
1346     if (referenceList != null) {
1347       for (PsiJavaCodeReferenceElement referenceElement : referenceList.getReferenceElements()) {
1348         if (!referenceElement.isQualified()) {
1349           final PsiElement superClass = referenceElement.resolve();
1350           if (superClass instanceof PsiClass) {
1351             final PsiClass superContainingClass = ((PsiClass)superClass).getContainingClass();
1352             if (superContainingClass != null && 
1353                 InheritanceUtil.isInheritorOrSelf(containingClass, superContainingClass, true) && 
1354                 !PsiTreeUtil.isAncestor(superContainingClass, containingClass, true)) {
1355               return true;
1356             }
1357           }
1358         }
1359       }
1360     }
1361     return false;
1362   }
1363
1364   private static void registerVariableParameterizedTypeFixes(@Nullable HighlightInfo highlightInfo,
1365                                                              @NotNull PsiVariable variable,
1366                                                              @NotNull PsiReferenceParameterList parameterList,
1367                                                              @NotNull JavaSdkVersion version) {
1368     PsiType type = variable.getType();
1369     if (!(type instanceof PsiClassType) || highlightInfo == null) return;
1370
1371     if (DumbService.getInstance(variable.getProject()).isDumb()) return;
1372
1373     String shortName = ((PsiClassType)type).getClassName();
1374     PsiManager manager = parameterList.getManager();
1375     final JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
1376     PsiShortNamesCache shortNamesCache = PsiShortNamesCache.getInstance(parameterList.getProject());
1377     PsiClass[] classes = shortNamesCache.getClassesByName(shortName, GlobalSearchScope.allScope(manager.getProject()));
1378     PsiElementFactory factory = facade.getElementFactory();
1379     for (PsiClass aClass : classes) {
1380       if (checkReferenceTypeArgumentList(aClass, parameterList, PsiSubstitutor.EMPTY, false, version) == null) {
1381         PsiType[] actualTypeParameters = parameterList.getTypeArguments();
1382         PsiTypeParameter[] classTypeParameters = aClass.getTypeParameters();
1383         Map<PsiTypeParameter, PsiType> map = new java.util.HashMap<>();
1384         for (int j = 0; j < classTypeParameters.length; j++) {
1385           PsiTypeParameter classTypeParameter = classTypeParameters[j];
1386           PsiType actualTypeParameter = actualTypeParameters[j];
1387           map.put(classTypeParameter, actualTypeParameter);
1388         }
1389         PsiSubstitutor substitutor = factory.createSubstitutor(map);
1390         PsiType suggestedType = factory.createType(aClass, substitutor);
1391         HighlightUtil.registerChangeVariableTypeFixes(variable, suggestedType, variable.getInitializer(), highlightInfo);
1392       }
1393     }
1394   }
1395
1396   static HighlightInfo checkInferredIntersections(PsiSubstitutor substitutor, TextRange ref) {
1397     for (Map.Entry<PsiTypeParameter, PsiType> typeEntry : substitutor.getSubstitutionMap().entrySet()) {
1398       final String parameterName = typeEntry.getKey().getName();
1399       final PsiType type = typeEntry.getValue();
1400       if (type instanceof PsiIntersectionType) {
1401         final String conflictingConjunctsMessage = ((PsiIntersectionType)type).getConflictingConjunctsMessage();
1402         if (conflictingConjunctsMessage != null) {
1403           return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
1404             .descriptionAndTooltip("Type parameter " + parameterName + " has incompatible upper bounds: " + conflictingConjunctsMessage)
1405             .range(ref).create();
1406         }
1407       }
1408     }
1409     return null;
1410   }
1411
1412   static HighlightInfo areSupersAccessible(@NotNull PsiClass aClass) {
1413     return areSupersAccessible(aClass, aClass.getResolveScope(), HighlightNamesUtil.getClassDeclarationTextRange(aClass), true);
1414   }
1415
1416   static HighlightInfo areSupersAccessible(@NotNull PsiClass aClass, PsiReferenceExpression ref) {
1417     final GlobalSearchScope resolveScope = ref.getResolveScope();
1418     final HighlightInfo info = areSupersAccessible(aClass, resolveScope, ref.getTextRange(), false);
1419     if (info != null) {
1420       return info;
1421     }
1422
1423     String message = null;
1424     final PsiElement parent = ref.getParent();
1425     if (parent instanceof PsiMethodCallExpression) {
1426       final JavaResolveResult resolveResult = ((PsiMethodCallExpression)parent).resolveMethodGenerics();
1427       final PsiMethod method = (PsiMethod)resolveResult.getElement();
1428       if (method != null) {
1429         final HashSet<PsiClass> classes = new HashSet<>();
1430         final JavaPsiFacade facade = JavaPsiFacade.getInstance(aClass.getProject());
1431         final PsiSubstitutor substitutor = resolveResult.getSubstitutor();
1432         
1433         message = isSuperTypeAccessible(substitutor.substitute(method.getReturnType()), classes, false, resolveScope, facade);
1434         if (message == null) {
1435           for (PsiType type : method.getSignature(substitutor).getParameterTypes()) {
1436             
1437             message = isSuperTypeAccessible(type, classes, false, resolveScope, facade);
1438             if (message != null) {
1439               break;
1440             }
1441           }
1442         }
1443       }
1444     }
1445     else {
1446       final PsiElement resolve = ref.resolve();
1447       if (resolve instanceof PsiField) {
1448         message = isSuperTypeAccessible(((PsiField)resolve).getType(), new HashSet<>(), false, resolveScope, JavaPsiFacade.getInstance(aClass.getProject()));
1449       }
1450     }
1451
1452     if (message != null) {
1453       return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
1454         .descriptionAndTooltip(message)
1455         .range(ref.getTextRange())
1456         .create();
1457     }
1458
1459     
1460     return null;
1461   }
1462
1463   private static HighlightInfo areSupersAccessible(@NotNull PsiClass aClass,
1464                                                    GlobalSearchScope resolveScope,
1465                                                    TextRange range,
1466                                                    boolean checkParameters) {
1467     final JavaPsiFacade factory = JavaPsiFacade.getInstance(aClass.getProject());
1468     for (PsiClassType superType : aClass.getSuperTypes()) {
1469       final String notAccessibleErrorMessage = isSuperTypeAccessible(superType, new HashSet<>(), checkParameters, resolveScope, factory);
1470       if (notAccessibleErrorMessage != null) {
1471         return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
1472           .descriptionAndTooltip(notAccessibleErrorMessage)
1473           .range(range)
1474           .create();
1475       }
1476     }
1477     return null;
1478   }
1479
1480   @Nullable
1481   private static String isSuperTypeAccessible(PsiType superType,
1482                                               HashSet<PsiClass> classes,
1483                                               boolean checkParameters, 
1484                                               GlobalSearchScope resolveScope,
1485                                               JavaPsiFacade factory) {
1486     final PsiClass aClass = PsiUtil.resolveClassInType(superType);
1487     if (aClass != null && classes.add(aClass)) {
1488       VirtualFile vFile = PsiUtilCore.getVirtualFile(aClass);
1489       if (vFile == null) {
1490         return null;
1491       }
1492       FileIndexFacade index = FileIndexFacade.getInstance(aClass.getProject());
1493       if (!index.isInSource(vFile) && !index.isInLibraryClasses(vFile)) {
1494         return null;
1495       }
1496
1497       final String qualifiedName = aClass.getQualifiedName();
1498       if (qualifiedName != null && factory.findClass(qualifiedName, resolveScope) == null) {
1499         return "Cannot access " + HighlightUtil.formatClass(aClass);
1500       }
1501
1502       if (checkParameters) {
1503         boolean isInLibrary = !index.isInContent(vFile);
1504         if (superType instanceof PsiClassType) {
1505           for (PsiType psiType : ((PsiClassType)superType).getParameters()) {
1506             final String notAccessibleMessage = isSuperTypeAccessible(psiType, classes, true, resolveScope, factory);
1507             if (notAccessibleMessage != null) {
1508               return notAccessibleMessage;
1509             }
1510           }
1511         }
1512
1513         for (PsiClassType type : aClass.getSuperTypes()) {
1514           final String notAccessibleMessage = isSuperTypeAccessible(type, classes, !isInLibrary, resolveScope, factory);
1515           if (notAccessibleMessage != null) {
1516             return notAccessibleMessage;
1517           }
1518         }
1519       }
1520     }
1521     return null;
1522   }
1523
1524   static HighlightInfo checkTypeParameterOverrideEquivalentMethods(PsiClass aClass, LanguageLevel level) {
1525     if (aClass instanceof PsiTypeParameter && level.isAtLeast(LanguageLevel.JDK_1_7)) {
1526       final PsiReferenceList extendsList = aClass.getExtendsList();
1527       if (extendsList != null && extendsList.getReferenceElements().length > 1) {
1528         //todo suppress erased methods which come from the same class
1529         final Collection<HighlightInfo> result = checkOverrideEquivalentMethods(aClass);
1530         if (result != null && !result.isEmpty()) {
1531           return result.iterator().next();
1532         }
1533       }
1534     }
1535     return null;
1536   }
1537 }
1538