f1c3a3655f18bdc0f35904fab5decd4e5f6cde71
[idea/community.git] / java / java-psi-impl / src / com / intellij / psi / impl / PsiClassImplUtil.java
1 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.psi.impl;
3
4 import com.intellij.lang.java.JavaLanguage;
5 import com.intellij.openapi.components.ServiceManager;
6 import com.intellij.openapi.diagnostic.Logger;
7 import com.intellij.openapi.progress.ProgressIndicatorProvider;
8 import com.intellij.openapi.progress.ProgressManager;
9 import com.intellij.openapi.roots.FileIndexFacade;
10 import com.intellij.openapi.util.*;
11 import com.intellij.openapi.util.registry.Registry;
12 import com.intellij.openapi.vfs.VirtualFile;
13 import com.intellij.pom.java.LanguageLevel;
14 import com.intellij.psi.*;
15 import com.intellij.psi.impl.java.stubs.PsiClassReferenceListStub;
16 import com.intellij.psi.impl.source.ClassInnerStuffCache;
17 import com.intellij.psi.impl.source.PsiImmediateClassType;
18 import com.intellij.psi.infos.MethodCandidateInfo;
19 import com.intellij.psi.scope.ElementClassFilter;
20 import com.intellij.psi.scope.ElementClassHint;
21 import com.intellij.psi.scope.NameHint;
22 import com.intellij.psi.scope.PsiScopeProcessor;
23 import com.intellij.psi.scope.processor.FilterScopeProcessor;
24 import com.intellij.psi.scope.processor.MethodResolverProcessor;
25 import com.intellij.psi.search.GlobalSearchScope;
26 import com.intellij.psi.search.LocalSearchScope;
27 import com.intellij.psi.search.PackageScope;
28 import com.intellij.psi.search.SearchScope;
29 import com.intellij.psi.stubs.StubElement;
30 import com.intellij.psi.util.*;
31 import com.intellij.ui.IconDeferrer;
32 import com.intellij.ui.RowIcon;
33 import com.intellij.util.*;
34 import com.intellij.util.containers.ConcurrentFactoryMap;
35 import com.intellij.util.containers.ContainerUtil;
36 import gnu.trove.THashMap;
37 import gnu.trove.THashSet;
38 import org.jetbrains.annotations.NonNls;
39 import org.jetbrains.annotations.NotNull;
40 import org.jetbrains.annotations.Nullable;
41
42 import javax.swing.*;
43 import java.util.*;
44 import java.util.concurrent.ConcurrentMap;
45
46 /**
47  * @author ik
48  */
49 public class PsiClassImplUtil {
50   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.PsiClassImplUtil");
51   private static final Key<ParameterizedCachedValue<Map<GlobalSearchScope, MembersMap>, PsiClass>> MAP_IN_CLASS_KEY = Key.create("MAP_KEY");
52   private static final String VALUES_METHOD = "values";
53   private static final String VALUE_OF_METHOD = "valueOf";
54
55   private PsiClassImplUtil() { }
56
57   @NotNull
58   public static PsiField[] getAllFields(@NotNull PsiClass aClass) {
59     List<PsiField> map = getAllByMap(aClass, MemberType.FIELD);
60     return map.toArray(PsiField.EMPTY_ARRAY);
61   }
62
63   @NotNull
64   public static PsiMethod[] getAllMethods(@NotNull PsiClass aClass) {
65     List<PsiMethod> methods = getAllByMap(aClass, MemberType.METHOD);
66     return methods.toArray(PsiMethod.EMPTY_ARRAY);
67   }
68
69   @NotNull
70   public static PsiClass[] getAllInnerClasses(@NotNull PsiClass aClass) {
71     List<PsiClass> classes = getAllByMap(aClass, MemberType.CLASS);
72     return classes.toArray(PsiClass.EMPTY_ARRAY);
73   }
74
75   @Nullable
76   public static PsiField findFieldByName(@NotNull PsiClass aClass, String name, boolean checkBases) {
77     List<PsiMember> byMap = findByMap(aClass, name, checkBases, MemberType.FIELD);
78     return byMap.isEmpty() ? null : (PsiField)byMap.get(0);
79   }
80
81   @NotNull
82   public static PsiMethod[] findMethodsByName(@NotNull PsiClass aClass, String name, boolean checkBases) {
83     List<PsiMember> methods = findByMap(aClass, name, checkBases, MemberType.METHOD);
84     //noinspection SuspiciousToArrayCall
85     return methods.toArray(PsiMethod.EMPTY_ARRAY);
86   }
87
88   @Nullable
89   public static PsiMethod findMethodBySignature(@NotNull PsiClass aClass, @NotNull PsiMethod patternMethod, final boolean checkBases) {
90     final List<PsiMethod> result = findMethodsBySignature(aClass, patternMethod, checkBases, true);
91     return result.isEmpty() ? null : result.get(0);
92   }
93
94   // ----------------------------- findMethodsBySignature -----------------------------------
95
96   @NotNull
97   public static PsiMethod[] findMethodsBySignature(@NotNull PsiClass aClass, @NotNull PsiMethod patternMethod, final boolean checkBases) {
98     List<PsiMethod> methods = findMethodsBySignature(aClass, patternMethod, checkBases, false);
99     return methods.toArray(PsiMethod.EMPTY_ARRAY);
100   }
101
102   @NotNull
103   private static List<PsiMethod> findMethodsBySignature(@NotNull PsiClass aClass,
104                                                         @NotNull PsiMethod patternMethod,
105                                                         boolean checkBases,
106                                                         boolean stopOnFirst) {
107     final PsiMethod[] methodsByName = aClass.findMethodsByName(patternMethod.getName(), checkBases);
108     if (methodsByName.length == 0) return Collections.emptyList();
109     final List<PsiMethod> methods = new SmartList<>();
110     final MethodSignature patternSignature = patternMethod.getSignature(PsiSubstitutor.EMPTY);
111     for (final PsiMethod method : methodsByName) {
112       final PsiClass superClass = method.getContainingClass();
113       final PsiSubstitutor substitutor = checkBases && !aClass.equals(superClass) && superClass != null ?
114                                          TypeConversionUtil.getSuperClassSubstitutor(superClass, aClass, PsiSubstitutor.EMPTY) :
115                                          PsiSubstitutor.EMPTY;
116       final MethodSignature signature = method.getSignature(substitutor);
117       if (signature.equals(patternSignature)) {
118         methods.add(method);
119         if (stopOnFirst) {
120           break;
121         }
122       }
123     }
124     return methods;
125   }
126
127   // ----------------------------------------------------------------------------------------
128
129   @Nullable
130   public static PsiClass findInnerByName(@NotNull PsiClass aClass, String name, boolean checkBases) {
131     List<PsiMember> byMap = findByMap(aClass, name, checkBases, MemberType.CLASS);
132     return byMap.isEmpty() ? null : (PsiClass)byMap.get(0);
133   }
134
135   @NotNull
136   private static List<PsiMember> findByMap(@NotNull PsiClass aClass, String name, boolean checkBases, @NotNull MemberType type) {
137     if (name == null) return Collections.emptyList();
138
139     if (checkBases) {
140       PsiMember[] list = getMap(aClass, type).get(name);
141       if (list == null) return Collections.emptyList();
142       return Arrays.asList(list);
143     }
144     else {
145       PsiMember[] members = null;
146       switch (type) {
147         case METHOD:
148           members = aClass.getMethods();
149           break;
150         case CLASS:
151           members = aClass.getInnerClasses();
152           break;
153         case FIELD:
154           members = aClass.getFields();
155           break;
156       }
157
158       List<PsiMember> list = new ArrayList<>();
159       for (PsiMember member : members) {
160         if (name.equals(member.getName())) {
161           list.add(member);
162         }
163       }
164       return list;
165     }
166   }
167
168   @NotNull
169   public static <T extends PsiMember> List<Pair<T, PsiSubstitutor>> getAllWithSubstitutorsByMap(@NotNull PsiClass aClass, @NotNull MemberType type) {
170     return withSubstitutors(aClass, getMap(aClass, type).get(ALL));
171   }
172
173   @NotNull
174   private static <T extends PsiMember> List<T> getAllByMap(@NotNull PsiClass aClass, @NotNull MemberType type) {
175     List<Pair<T, PsiSubstitutor>> pairs = getAllWithSubstitutorsByMap(aClass, type);
176
177     final List<T> ret = new ArrayList<>(pairs.size());
178     //noinspection ForLoopReplaceableByForEach
179     for (int i = 0; i < pairs.size(); i++) {
180       Pair<T, PsiSubstitutor> pair = pairs.get(i);
181       T t = pair.getFirst();
182       LOG.assertTrue(t != null, aClass);
183       ret.add(t);
184     }
185     return ret;
186   }
187
188   @NonNls private static final String ALL = "Intellij-IDEA-ALL";
189
190   public enum MemberType {CLASS, FIELD, METHOD}
191
192   private static Map<String, PsiMember[]> getMap(@NotNull PsiClass aClass, @NotNull MemberType type) {
193     ParameterizedCachedValue<Map<GlobalSearchScope, MembersMap>, PsiClass> value = getValues(aClass);
194     return value.getValue(aClass).get(aClass.getResolveScope()).get(type);
195   }
196
197   @NotNull
198   private static ParameterizedCachedValue<Map<GlobalSearchScope, MembersMap>, PsiClass> getValues(@NotNull PsiClass aClass) {
199     ParameterizedCachedValue<Map<GlobalSearchScope, MembersMap>, PsiClass> value = aClass.getUserData(MAP_IN_CLASS_KEY);
200     if (value == null) {
201       value = CachedValuesManager.getManager(aClass.getProject()).createParameterizedCachedValue(ByNameCachedValueProvider.INSTANCE, false);
202       //Do not cache for nonphysical elements
203       if (aClass.isPhysical()) {
204         value = ((UserDataHolderEx)aClass).putUserDataIfAbsent(MAP_IN_CLASS_KEY, value);
205       }
206     }
207     return value;
208   }
209
210   private static class ClassIconRequest {
211     @NotNull private final PsiClass psiClass;
212     private final int flags;
213     private final Icon symbolIcon;
214
215     private ClassIconRequest(@NotNull PsiClass psiClass, int flags, Icon symbolIcon) {
216       this.psiClass = psiClass;
217       this.flags = flags;
218       this.symbolIcon = symbolIcon;
219     }
220
221     @Override
222     public boolean equals(Object o) {
223       if (this == o) return true;
224       if (!(o instanceof ClassIconRequest)) return false;
225
226       ClassIconRequest that = (ClassIconRequest)o;
227
228       return flags == that.flags && psiClass.equals(that.psiClass);
229     }
230
231     @Override
232     public int hashCode() {
233       int result = psiClass.hashCode();
234       result = 31 * result + flags;
235       return result;
236     }
237   }
238
239   private static final Function<ClassIconRequest, Icon> FULL_ICON_EVALUATOR = (NullableFunction<ClassIconRequest, Icon>)r -> {
240     if (!r.psiClass.isValid() || r.psiClass.getProject().isDisposed()) return null;
241
242     final boolean isLocked = BitUtil.isSet(r.flags, Iconable.ICON_FLAG_READ_STATUS) && !r.psiClass.isWritable();
243     Icon symbolIcon = r.symbolIcon != null
244                       ? r.symbolIcon
245                       : ElementPresentationUtil.getClassIconOfKind(r.psiClass, ElementPresentationUtil.getClassKind(r.psiClass));
246     RowIcon baseIcon = ElementPresentationUtil.createLayeredIcon(symbolIcon, r.psiClass, isLocked);
247     Icon result = ElementPresentationUtil.addVisibilityIcon(r.psiClass, r.flags, baseIcon);
248     Iconable.LastComputedIcon.put(r.psiClass, result, r.flags);
249     return result;
250   };
251
252   public static Icon getClassIcon(final int flags, @NotNull PsiClass aClass) {
253     return getClassIcon(flags, aClass, null);
254   }
255
256   public static Icon getClassIcon(int flags, @NotNull PsiClass aClass, @Nullable Icon symbolIcon) {
257     Icon base = Iconable.LastComputedIcon.get(aClass, flags);
258     if (base == null) {
259       if (symbolIcon == null) {
260         symbolIcon = ElementPresentationUtil.getClassIconOfKind(aClass, ElementPresentationUtil.getBasicClassKind(aClass));
261       }
262       RowIcon baseIcon = ElementBase.createLayeredIcon(aClass, symbolIcon, 0);
263       base = ElementPresentationUtil.addVisibilityIcon(aClass, flags, baseIcon);
264     }
265
266     return IconDeferrer.getInstance().defer(base, new ClassIconRequest(aClass, flags, symbolIcon), FULL_ICON_EVALUATOR);
267   }
268
269   @NotNull
270   public static SearchScope getClassUseScope(@NotNull PsiClass aClass) {
271     if (aClass instanceof PsiAnonymousClass) {
272       return new LocalSearchScope(aClass);
273     }
274     final GlobalSearchScope maximalUseScope = ResolveScopeManager.getElementUseScope(aClass);
275     PsiFile file = aClass.getContainingFile();
276     if (PsiImplUtil.isInServerPage(file)) return maximalUseScope;
277     final PsiClass containingClass = aClass.getContainingClass();
278     if (aClass.hasModifierProperty(PsiModifier.PUBLIC) ||
279         aClass.hasModifierProperty(PsiModifier.PROTECTED)) {
280       return containingClass == null ? maximalUseScope : containingClass.getUseScope();
281     }
282     else if (aClass.hasModifierProperty(PsiModifier.PRIVATE) || aClass instanceof PsiTypeParameter) {
283       PsiClass topClass = PsiUtil.getTopLevelClass(aClass);
284       return new LocalSearchScope(topClass == null ? aClass.getContainingFile() : topClass);
285     }
286     else {
287       PsiPackage aPackage = null;
288       if (file instanceof PsiJavaFile) {
289         aPackage = JavaPsiFacade.getInstance(aClass.getProject()).findPackage(((PsiJavaFile)file).getPackageName());
290       }
291
292       if (aPackage == null) {
293         PsiDirectory dir = file.getContainingDirectory();
294         if (dir != null) {
295           aPackage = JavaDirectoryService.getInstance().getPackage(dir);
296         }
297       }
298
299       if (aPackage != null) {
300         SearchScope scope = PackageScope.packageScope(aPackage, false);
301         scope = scope.intersectWith(maximalUseScope);
302         return scope;
303       }
304
305       return new LocalSearchScope(file);
306     }
307   }
308
309   public static boolean isMainOrPremainMethod(@NotNull PsiMethod method) {
310     if (!PsiType.VOID.equals(method.getReturnType())) return false;
311     String name = method.getName();
312     if (!("main".equals(name) || "premain".equals(name) || "agentmain".equals(name))) return false;
313
314     PsiElementFactory factory = JavaPsiFacade.getElementFactory(method.getProject());
315     MethodSignature signature = method.getSignature(PsiSubstitutor.EMPTY);
316     try {
317       MethodSignature main = createSignatureFromText(factory, "void main(String[] args);");
318       if (MethodSignatureUtil.areSignaturesEqual(signature, main)) return true;
319       MethodSignature premain = createSignatureFromText(factory, "void premain(String args, java.lang.instrument.Instrumentation i);");
320       if (MethodSignatureUtil.areSignaturesEqual(signature, premain)) return true;
321       MethodSignature agentmain = createSignatureFromText(factory, "void agentmain(String args, java.lang.instrument.Instrumentation i);");
322       if (MethodSignatureUtil.areSignaturesEqual(signature, agentmain)) return true;
323     }
324     catch (IncorrectOperationException e) {
325       LOG.error(e);
326     }
327
328     return false;
329   }
330
331   @NotNull
332   private static MethodSignature createSignatureFromText(@NotNull PsiElementFactory factory, @NotNull String text) {
333     return factory.createMethodFromText(text, null).getSignature(PsiSubstitutor.EMPTY);
334   }
335
336   private static class MembersMap {
337     final ConcurrentMap<MemberType, Map<String, PsiMember[]>> myMap;
338
339     MembersMap(PsiClass psiClass, GlobalSearchScope scope) {
340       myMap = createMembersMap(psiClass, scope);
341     }
342
343     private Map<String, PsiMember[]> get(MemberType type) {
344       return myMap.get(type);
345     }
346   }
347
348   private static ConcurrentMap<MemberType, Map<String, PsiMember[]>> createMembersMap(PsiClass psiClass, GlobalSearchScope scope) {
349     return ConcurrentFactoryMap.createMap(key -> {
350       final Map<String, List<PsiMember>> map = new THashMap<>();
351
352       final List<PsiMember> allMembers = new ArrayList<>();
353       map.put(ALL, allMembers);
354
355       ElementClassFilter filter = key == MemberType.CLASS ? ElementClassFilter.CLASS :
356                                   key == MemberType.METHOD ? ElementClassFilter.METHOD :
357                                   ElementClassFilter.FIELD;
358       final ElementClassHint classHint = kind -> key == MemberType.CLASS && kind == ElementClassHint.DeclarationKind.CLASS ||
359              key == MemberType.FIELD && (kind == ElementClassHint.DeclarationKind.FIELD || kind == ElementClassHint.DeclarationKind.ENUM_CONST) ||
360              key == MemberType.METHOD && kind == ElementClassHint.DeclarationKind.METHOD;
361       FilterScopeProcessor<MethodCandidateInfo> processor = new FilterScopeProcessor<MethodCandidateInfo>(filter) {
362         @Override
363         protected void add(@NotNull PsiElement element, @NotNull PsiSubstitutor substitutor) {
364           if (key == MemberType.CLASS && element instanceof PsiClass ||
365               key == MemberType.METHOD && element instanceof PsiMethod ||
366               key == MemberType.FIELD && element instanceof PsiField) {
367             PsiUtilCore.ensureValid(element);
368             allMembers.add((PsiMember)element);
369             String currentName = ((PsiMember)element).getName();
370             List<PsiMember> listByName = map.computeIfAbsent(currentName, __ -> ContainerUtil.newSmartList());
371             listByName.add((PsiMember)element);
372           }
373         }
374
375         @Override
376         public <K> K getHint(@NotNull Key<K> hintKey) {
377           //noinspection unchecked
378           return ElementClassHint.KEY == hintKey ? (K)classHint : super.getHint(hintKey);
379         }
380       };
381
382       processDeclarationsInClassNotCached(psiClass, processor, ResolveState.initial(), null, null, psiClass, false,
383                                           PsiUtil.getLanguageLevel(psiClass), scope);
384       Map<String, PsiMember[]> result = new THashMap<>();
385       for (Map.Entry<String, List<PsiMember>> entry : map.entrySet()) {
386         result.put(entry.getKey(), entry.getValue().toArray(PsiMember.EMPTY_ARRAY));
387       }
388       return result;
389     });
390   }
391
392   private static class ByNameCachedValueProvider implements ParameterizedCachedValueProvider<Map<GlobalSearchScope, MembersMap>, PsiClass> {
393     private static final ByNameCachedValueProvider INSTANCE = new ByNameCachedValueProvider();
394
395     @Override
396     public CachedValueProvider.Result<Map<GlobalSearchScope, MembersMap>> compute(@NotNull final PsiClass myClass) {
397       Map<GlobalSearchScope, MembersMap> map = ConcurrentFactoryMap.createMap(scope -> new MembersMap(myClass, scope));
398       return CachedValueProvider.Result.create(map, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT);
399     }
400   }
401
402   public static boolean processDeclarationsInEnum(@NotNull PsiScopeProcessor processor,
403                                                   @NotNull ResolveState state,
404                                                   @NotNull ClassInnerStuffCache innerStuffCache) {
405     ElementClassHint classHint = processor.getHint(ElementClassHint.KEY);
406     if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.METHOD)) {
407       NameHint nameHint = processor.getHint(NameHint.KEY);
408       if (nameHint == null || VALUES_METHOD.equals(nameHint.getName(state))) {
409         PsiMethod method = innerStuffCache.getValuesMethod();
410         if (method != null && !processor.execute(method, ResolveState.initial())) return false;
411       }
412       if (nameHint == null || VALUE_OF_METHOD.equals(nameHint.getName(state))) {
413         PsiMethod method = innerStuffCache.getValueOfMethod();
414         if (method != null && !processor.execute(method, ResolveState.initial())) return false;
415       }
416     }
417
418     return true;
419   }
420
421   public static boolean processDeclarationsInClass(@NotNull PsiClass aClass,
422                                                    @NotNull final PsiScopeProcessor processor,
423                                                    @NotNull ResolveState state,
424                                                    @Nullable Set<PsiClass> visited,
425                                                    PsiElement last,
426                                                    @NotNull PsiElement place,
427                                                    @NotNull LanguageLevel languageLevel,
428                                                    boolean isRaw) {
429     return processDeclarationsInClass(aClass, processor, state, visited, last, place, languageLevel, isRaw, place.getResolveScope());
430   }
431
432   private static boolean processDeclarationsInClass(@NotNull PsiClass aClass,
433                                                     @NotNull final PsiScopeProcessor processor,
434                                                     @NotNull ResolveState state,
435                                                     @Nullable Set<PsiClass> visited,
436                                                     PsiElement last,
437                                                     @NotNull PsiElement place,
438                                                     @NotNull LanguageLevel languageLevel,
439                                                     boolean isRaw,
440                                                     @NotNull GlobalSearchScope resolveScope) {
441     if (last instanceof PsiTypeParameterList || last instanceof PsiModifierList && aClass.getModifierList() == last) {
442       return true;
443     }
444     if (visited != null && visited.contains(aClass)) return true;
445
446     PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY);
447     isRaw = isRaw || PsiUtil.isRawSubstitutor(aClass, substitutor);
448
449     final NameHint nameHint = processor.getHint(NameHint.KEY);
450     if (nameHint != null) {
451       String name = nameHint.getName(state);
452       return processCachedMembersByName(aClass, processor, state, visited, last, place, isRaw, substitutor,
453                                         getValues(aClass).getValue(aClass).get(resolveScope), name, languageLevel, resolveScope);
454     }
455     return processDeclarationsInClassNotCached(aClass, processor, state, visited, last, place, isRaw, languageLevel, resolveScope);
456   }
457
458   private static boolean processCachedMembersByName(@NotNull final PsiClass aClass,
459                                                     @NotNull PsiScopeProcessor processor,
460                                                     @NotNull ResolveState state,
461                                                     @Nullable Set<? super PsiClass> visited,
462                                                     PsiElement last,
463                                                     @NotNull final PsiElement place,
464                                                     final boolean isRaw,
465                                                     @NotNull final PsiSubstitutor substitutor,
466                                                     @NotNull MembersMap value,
467                                                     String name,
468                                                     @NotNull final LanguageLevel languageLevel,
469                                                     final GlobalSearchScope resolveScope) {
470     Function<PsiMember, PsiSubstitutor> finalSubstitutor = new Function<PsiMember, PsiSubstitutor>() {
471       final ScopedClassHierarchy hierarchy = ScopedClassHierarchy.getHierarchy(aClass, resolveScope);
472       final PsiElementFactory factory = JavaPsiFacade.getElementFactory(aClass.getProject());
473       @Override
474       public PsiSubstitutor fun(PsiMember member) {
475         PsiClass containingClass = ObjectUtils.assertNotNull(member.getContainingClass());
476         PsiSubstitutor superSubstitutor = hierarchy.getSuperMembersSubstitutor(containingClass, languageLevel);
477         PsiSubstitutor finalSubstitutor = obtainFinalSubstitutor(containingClass,
478                                                                  superSubstitutor == null ? PsiSubstitutor.EMPTY : superSubstitutor,
479                                                                  aClass, substitutor, factory, languageLevel);
480         return member instanceof PsiMethod ? checkRaw(isRaw, factory, (PsiMethod)member, finalSubstitutor) : finalSubstitutor;
481       }
482     };
483
484     final ElementClassHint classHint = processor.getHint(ElementClassHint.KEY);
485
486     if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.FIELD)) {
487       final PsiField fieldByName = aClass.findFieldByName(name, false);
488       if (fieldByName != null) {
489         PsiUtilCore.ensureValid(fieldByName);
490         processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
491         if (!processor.execute(fieldByName, state)) return false;
492       }
493       else {
494         final Map<String, PsiMember[]> allFieldsMap = value.get(MemberType.FIELD);
495
496         final PsiMember[] list = allFieldsMap.get(name);
497         if (list != null) {
498           boolean resolved = false;
499           for (final PsiMember candidateField : list) {
500             PsiClass containingClass = candidateField.getContainingClass();
501             PsiUtilCore.ensureValid(candidateField);
502             if (containingClass == null) {
503               PsiElement parent = candidateField.getParent();
504               LOG.error("No class for field " + candidateField.getName() + " of " + candidateField.getClass() +
505                         ", parent " + parent + " of " + (parent == null ? null : parent.getClass()));
506               continue;
507             }
508
509             processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, containingClass);
510             if (!processor.execute(candidateField, state.put(PsiSubstitutor.KEY, finalSubstitutor.fun(candidateField)))) {
511               resolved = true;
512             }
513           }
514           if (resolved) return false;
515         }
516       }
517     }
518     if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) {
519       if (last != null && last.getContext() == aClass) {
520         if (last instanceof PsiClass) {
521           if (!processor.execute(last, state)) return false;
522         }
523         // Parameters
524         final PsiTypeParameterList list = aClass.getTypeParameterList();
525         if (list != null && !list.processDeclarations(processor, state, last, place)) return false;
526       }
527       if (!(last instanceof PsiReferenceList)) {
528         final PsiClass classByName = aClass.findInnerClassByName(name, false);
529         if (classByName != null) {
530           PsiUtilCore.ensureValid(classByName);
531           processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
532           if (!processor.execute(classByName, state)) return false;
533         }
534         else {
535           Map<String, PsiMember[]> allClassesMap = value.get(MemberType.CLASS);
536
537           PsiMember[] list = allClassesMap.get(name);
538           if (list != null) {
539             boolean resolved = false;
540             for (final PsiMember inner : list) {
541               PsiUtilCore.ensureValid(inner);
542               PsiClass containingClass = inner.getContainingClass();
543               if (containingClass != null) {
544                 processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, containingClass);
545                 if (!processor.execute(inner, state.put(PsiSubstitutor.KEY, finalSubstitutor.fun(inner)))) {
546                   resolved = true;
547                 }
548               }
549             }
550             if (resolved) return false;
551           }
552         }
553       }
554     }
555     if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.METHOD)) {
556       if (processor instanceof MethodResolverProcessor) {
557         final MethodResolverProcessor methodResolverProcessor = (MethodResolverProcessor)processor;
558         if (methodResolverProcessor.isConstructor()) {
559           final PsiMethod[] constructors = aClass.getConstructors();
560           methodResolverProcessor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
561           for (PsiMethod constructor : constructors) {
562             PsiUtilCore.ensureValid(constructor);
563             if (!methodResolverProcessor.execute(constructor, state)) return false;
564           }
565           return true;
566         }
567       }
568       Map<String, PsiMember[]> allMethodsMap = value.get(MemberType.METHOD);
569       PsiMember[] list = allMethodsMap.get(name);
570       if (list != null) {
571         boolean resolved = false;
572         for (final PsiMember candidate : list) {
573           ProgressIndicatorProvider.checkCanceled();
574           PsiMethod candidateMethod = (PsiMethod)candidate;
575           PsiUtilCore.ensureValid(candidateMethod);
576           if (processor instanceof MethodResolverProcessor) {
577             if (candidateMethod.isConstructor() != ((MethodResolverProcessor)processor).isConstructor()) continue;
578           }
579           final PsiClass containingClass = candidateMethod.getContainingClass();
580           if (containingClass == null || visited != null && visited.contains(containingClass)) {
581             continue;
582           }
583
584           processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, containingClass);
585           if (!processor.execute(candidateMethod, state.put(PsiSubstitutor.KEY, finalSubstitutor.fun(candidateMethod)))) {
586             resolved = true;
587           }
588         }
589         if (resolved) return false;
590
591         if (visited != null) {
592           for (PsiMember aList : list) {
593             visited.add(aList.getContainingClass());
594           }
595         }
596       }
597     }
598     return true;
599   }
600
601   private static PsiSubstitutor checkRaw(boolean isRaw,
602                                          @NotNull PsiElementFactory factory,
603                                          @NotNull PsiMethod candidateMethod,
604                                          @NotNull PsiSubstitutor substitutor) {
605     //4.8-2. Raw Types and Inheritance
606     //certain members of a raw type are not erased,
607     //namely static members whose types are parameterized, and members inherited from a non-generic supertype.
608     if (isRaw && !candidateMethod.hasModifierProperty(PsiModifier.STATIC)) {
609       final PsiClass containingClass = candidateMethod.getContainingClass();
610       if (containingClass != null && containingClass.hasTypeParameters()) {
611         PsiTypeParameter[] methodTypeParameters = candidateMethod.getTypeParameters();
612         substitutor = factory.createRawSubstitutor(substitutor, methodTypeParameters);
613       }
614     }
615     return substitutor;
616   }
617
618   public static PsiSubstitutor obtainFinalSubstitutor(@NotNull PsiClass candidateClass,
619                                                       @NotNull PsiSubstitutor candidateSubstitutor,
620                                                       @NotNull PsiClass aClass,
621                                                       @NotNull PsiSubstitutor substitutor,
622                                                       @NotNull PsiElementFactory elementFactory,
623                                                       @NotNull LanguageLevel languageLevel) {
624     if (PsiUtil.isRawSubstitutor(aClass, substitutor)) {
625       return elementFactory.createRawSubstitutor(candidateClass).putAll(substitutor);
626     }
627     final PsiType containingType = elementFactory.createType(candidateClass, candidateSubstitutor, languageLevel);
628     PsiType type = substitutor.substitute(containingType);
629     if (!(type instanceof PsiClassType)) return candidateSubstitutor;
630     return ((PsiClassType)type).resolveGenerics().getSubstitutor();
631   }
632
633   private static boolean processDeclarationsInClassNotCached(@NotNull PsiClass aClass,
634                                                              @NotNull final PsiScopeProcessor processor,
635                                                              @NotNull final ResolveState state,
636                                                              @Nullable Set<PsiClass> visited,
637                                                              final PsiElement last,
638                                                              @NotNull final PsiElement place,
639                                                              final boolean isRaw,
640                                                              @NotNull final LanguageLevel languageLevel,
641                                                              @NotNull final GlobalSearchScope resolveScope) {
642     ProgressManager.checkCanceled();
643     if (visited == null) visited = new THashSet<>();
644     if (!visited.add(aClass)) return true;
645     processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
646     final ElementClassHint classHint = processor.getHint(ElementClassHint.KEY);
647     final NameHint nameHint = processor.getHint(NameHint.KEY);
648
649
650     if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.FIELD)) {
651       if (nameHint != null) {
652         final PsiField fieldByName = aClass.findFieldByName(nameHint.getName(state), false);
653         if (fieldByName != null && !processor.execute(fieldByName, state)) return false;
654       }
655       else {
656         final PsiField[] fields = aClass.getFields();
657         for (final PsiField field : fields) {
658           if (!processor.execute(field, state)) return false;
659         }
660       }
661     }
662
663     PsiElementFactory factory = JavaPsiFacade.getElementFactory(aClass.getProject());
664
665     if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.METHOD)) {
666       PsiSubstitutor baseSubstitutor = state.get(PsiSubstitutor.KEY);
667       final PsiMethod[] methods = nameHint != null ? aClass.findMethodsByName(nameHint.getName(state), false) : aClass.getMethods();
668       for (final PsiMethod method : methods) {
669         PsiSubstitutor finalSubstitutor = checkRaw(isRaw, factory, method, baseSubstitutor);
670         ResolveState methodState = finalSubstitutor == baseSubstitutor ? state : state.put(PsiSubstitutor.KEY, finalSubstitutor);
671         if (!processor.execute(method, methodState)) return false;
672       }
673     }
674
675     if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) {
676       if (last != null && last.getContext() == aClass) {
677         // Parameters
678         final PsiTypeParameterList list = aClass.getTypeParameterList();
679         if (list != null && !list.processDeclarations(processor, ResolveState.initial(), last, place)) return false;
680       }
681
682       if (!(last instanceof PsiReferenceList) && !(last instanceof PsiModifierList)) {
683         // Inners
684         if (nameHint != null) {
685           final PsiClass inner = aClass.findInnerClassByName(nameHint.getName(state), false);
686           if (inner != null) {
687             if (!processor.execute(inner, state)) return false;
688           }
689         }
690         else {
691           final PsiClass[] inners = aClass.getInnerClasses();
692           for (final PsiClass inner : inners) {
693             if (!processor.execute(inner, state)) return false;
694           }
695         }
696       }
697     }
698
699     if (last instanceof PsiReferenceList) return true;
700
701     final Set<PsiClass> visited1 = visited;
702     return processSuperTypes(aClass, state.get(PsiSubstitutor.KEY), factory, languageLevel, resolveScope,
703                              (superClass, finalSubstitutor) -> processDeclarationsInClass(superClass, processor, state.put(PsiSubstitutor.KEY, finalSubstitutor), visited1, last, place,
704                                                                                                                languageLevel, isRaw, resolveScope));
705   }
706
707   @Nullable
708   public static <T extends PsiType> T correctType(@Nullable final T originalType, @NotNull final GlobalSearchScope resolveScope) {
709     if (originalType == null || !Registry.is("java.correct.class.type.by.place.resolve.scope")) {
710       return originalType;
711     }
712
713     return new TypeCorrector(resolveScope).correctType(originalType);
714   }
715
716   public static List<PsiClassType.ClassResolveResult> getScopeCorrectedSuperTypes(final PsiClass aClass, GlobalSearchScope resolveScope) {
717     PsiUtilCore.ensureValid(aClass);
718     return ScopedClassHierarchy.getHierarchy(aClass, resolveScope).getImmediateSupersWithCapturing();
719   }
720
721   static boolean processSuperTypes(@NotNull PsiClass aClass,
722                                    PsiSubstitutor substitutor,
723                                    @NotNull PsiElementFactory factory,
724                                    @NotNull LanguageLevel languageLevel,
725                                    GlobalSearchScope resolveScope,
726                                    PairProcessor<? super PsiClass, ? super PsiSubstitutor> processor) {
727     boolean resolved = false;
728     for (PsiClassType.ClassResolveResult superTypeResolveResult : getScopeCorrectedSuperTypes(aClass, resolveScope)) {
729       PsiClass superClass = superTypeResolveResult.getElement();
730       assert superClass != null;
731       PsiSubstitutor finalSubstitutor = obtainFinalSubstitutor(superClass, superTypeResolveResult.getSubstitutor(), aClass,
732                                                                substitutor, factory, languageLevel);
733       if (!processor.process(superClass, finalSubstitutor)) {
734         resolved = true;
735       }
736     }
737     return !resolved;
738   }
739
740   @Nullable
741   public static PsiClass getSuperClass(@NotNull PsiClass psiClass) {
742     if (psiClass.isInterface()) {
743       return findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_OBJECT);
744     }
745     if (psiClass.isEnum()) {
746       return findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_ENUM);
747     }
748
749     if (psiClass instanceof PsiAnonymousClass) {
750       PsiClassType baseClassReference = ((PsiAnonymousClass)psiClass).getBaseClassType();
751       PsiClass baseClass = baseClassReference.resolve();
752       if (baseClass == null || baseClass.isInterface()) return findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_OBJECT);
753       return baseClass;
754     }
755
756     if (CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass.getQualifiedName())) return null;
757
758     final PsiClassType[] referenceElements = psiClass.getExtendsListTypes();
759
760     if (referenceElements.length == 0) return findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_OBJECT);
761
762     PsiClass psiResolved = referenceElements[0].resolve();
763     return psiResolved == null ? findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_OBJECT) : psiResolved;
764   }
765
766   @Nullable
767   private static PsiClass findSpecialSuperClass(@NotNull PsiClass psiClass, String className) {
768     return JavaPsiFacade.getInstance(psiClass.getProject()).findClass(className, psiClass.getResolveScope());
769   }
770
771   @NotNull
772   public static PsiClass[] getSupers(@NotNull PsiClass psiClass) {
773     final PsiClass[] supers = getSupersInner(psiClass);
774     for (final PsiClass aSuper : supers) {
775       LOG.assertTrue(aSuper != null);
776     }
777     return supers;
778   }
779
780   @NotNull
781   private static PsiClass[] getSupersInner(@NotNull PsiClass psiClass) {
782     PsiClassType[] extendsListTypes = psiClass.getExtendsListTypes();
783
784     if (psiClass.isInterface()) {
785       return resolveClassReferenceList(extendsListTypes, psiClass, true);
786     }
787
788     if (psiClass instanceof PsiAnonymousClass) {
789       PsiAnonymousClass psiAnonymousClass = (PsiAnonymousClass)psiClass;
790       PsiClassType baseClassReference = psiAnonymousClass.getBaseClassType();
791       PsiClass baseClass = baseClassReference.resolve();
792       if (baseClass != null) {
793         if (baseClass.isInterface()) {
794           PsiClass objectClass = findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_OBJECT);
795           return objectClass != null ? new PsiClass[]{objectClass, baseClass} : new PsiClass[]{baseClass};
796         }
797         return new PsiClass[]{baseClass};
798       }
799
800       PsiClass objectClass = findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_OBJECT);
801       return objectClass != null ? new PsiClass[]{objectClass} : PsiClass.EMPTY_ARRAY;
802     }
803     if (psiClass instanceof PsiTypeParameter) {
804       if (extendsListTypes.length == 0) {
805         final PsiClass objectClass = findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_OBJECT);
806         return objectClass != null ? new PsiClass[]{objectClass} : PsiClass.EMPTY_ARRAY;
807       }
808       return resolveClassReferenceList(extendsListTypes, psiClass, false);
809     }
810
811     PsiClassType[] implementsListTypes = psiClass.getImplementsListTypes();
812     PsiClass[] interfaces = resolveClassReferenceList(implementsListTypes, psiClass, false);
813
814     PsiClass superClass = getSuperClass(psiClass);
815     if (superClass == null) return interfaces;
816     PsiClass[] types = new PsiClass[interfaces.length + 1];
817     types[0] = superClass;
818     System.arraycopy(interfaces, 0, types, 1, interfaces.length);
819
820     return types;
821   }
822
823   @NotNull
824   public static PsiClassType[] getSuperTypes(@NotNull PsiClass psiClass) {
825     if (psiClass instanceof PsiAnonymousClass) {
826       PsiClassType baseClassType = ((PsiAnonymousClass)psiClass).getBaseClassType();
827       PsiClass baseClass = baseClassType.resolve();
828       if (baseClass == null || !baseClass.isInterface()) {
829         return new PsiClassType[]{baseClassType};
830       }
831       else {
832         PsiClassType objectType = PsiType.getJavaLangObject(psiClass.getManager(), psiClass.getResolveScope());
833         return new PsiClassType[]{objectType, baseClassType};
834       }
835     }
836
837     PsiClassType[] extendsTypes = psiClass.getExtendsListTypes();
838     PsiClassType[] implementsTypes = psiClass.getImplementsListTypes();
839     boolean hasExtends = extendsTypes.length != 0;
840     int extendsListLength = extendsTypes.length + (hasExtends ? 0 : 1);
841     PsiClassType[] result = new PsiClassType[extendsListLength + implementsTypes.length];
842
843     System.arraycopy(extendsTypes, 0, result, 0, extendsTypes.length);
844     if (!hasExtends) {
845       if (CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass.getQualifiedName())) {
846         return PsiClassType.EMPTY_ARRAY;
847       }
848       PsiManager manager = psiClass.getManager();
849       PsiClassType objectType = PsiType.getJavaLangObject(manager, psiClass.getResolveScope());
850       result[0] = objectType;
851     }
852     System.arraycopy(implementsTypes, 0, result, extendsListLength, implementsTypes.length);
853     return result;
854   }
855
856   @NotNull
857   private static PsiClassType getAnnotationSuperType(@NotNull PsiClass psiClass, @NotNull PsiElementFactory factory) {
858     return factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_ANNOTATION_ANNOTATION, psiClass.getResolveScope());
859   }
860
861   private static PsiClassType getEnumSuperType(@NotNull PsiClass psiClass, @NotNull PsiElementFactory factory) {
862     PsiClassType superType;
863     final PsiClass enumClass = findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_ENUM);
864     if (enumClass == null) {
865       try {
866         superType = (PsiClassType)factory.createTypeFromText(CommonClassNames.JAVA_LANG_ENUM, null);
867       }
868       catch (IncorrectOperationException e) {
869         superType = null;
870       }
871     }
872     else {
873       final PsiTypeParameter[] typeParameters = enumClass.getTypeParameters();
874       PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
875       if (typeParameters.length == 1) {
876         substitutor = substitutor.put(typeParameters[0], factory.createType(psiClass));
877       }
878       superType = new PsiImmediateClassType(enumClass, substitutor);
879     }
880     return superType;
881   }
882
883   @NotNull
884   public static PsiClass[] getInterfaces(@NotNull PsiTypeParameter typeParameter) {
885     final PsiClassType[] referencedTypes = typeParameter.getExtendsListTypes();
886     if (referencedTypes.length == 0) {
887       return PsiClass.EMPTY_ARRAY;
888     }
889     final List<PsiClass> result = new ArrayList<>(referencedTypes.length);
890     for (PsiClassType referencedType : referencedTypes) {
891       final PsiClass psiClass = referencedType.resolve();
892       if (psiClass != null && psiClass.isInterface()) {
893         result.add(psiClass);
894       }
895     }
896     return result.toArray(PsiClass.EMPTY_ARRAY);
897   }
898
899   @NotNull
900   public static PsiClass[] getInterfaces(@NotNull PsiClass psiClass) {
901     if (psiClass.isInterface()) {
902       return resolveClassReferenceList(psiClass.getExtendsListTypes(), psiClass, false);
903     }
904
905     if (psiClass instanceof PsiAnonymousClass) {
906       PsiClassType baseClassReference = ((PsiAnonymousClass)psiClass).getBaseClassType();
907       PsiClass baseClass = baseClassReference.resolve();
908       return baseClass != null && baseClass.isInterface() ? new PsiClass[]{baseClass} : PsiClass.EMPTY_ARRAY;
909     }
910
911     final PsiClassType[] implementsListTypes = psiClass.getImplementsListTypes();
912     return resolveClassReferenceList(implementsListTypes, psiClass, false);
913   }
914
915   @NotNull
916   private static PsiClass[] resolveClassReferenceList(@NotNull PsiClassType[] listOfTypes,
917                                                       @NotNull PsiClass psiClass,
918                                                       boolean includeObject) {
919     PsiClass objectClass = null;
920     if (includeObject) {
921       objectClass = findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_OBJECT);
922       if (objectClass == null) includeObject = false;
923     }
924     if (listOfTypes.length == 0) {
925       if (includeObject) return new PsiClass[]{objectClass};
926       return PsiClass.EMPTY_ARRAY;
927     }
928
929     int referenceCount = listOfTypes.length;
930     if (includeObject) referenceCount++;
931
932     PsiClass[] resolved = new PsiClass[referenceCount];
933     int resolvedCount = 0;
934
935     if (includeObject) resolved[resolvedCount++] = objectClass;
936     for (PsiClassType reference : listOfTypes) {
937       PsiClass refResolved = reference.resolve();
938       if (refResolved != null) resolved[resolvedCount++] = refResolved;
939     }
940
941     if (resolvedCount < referenceCount) {
942       PsiClass[] shorter = new PsiClass[resolvedCount];
943       System.arraycopy(resolved, 0, shorter, 0, resolvedCount);
944       resolved = shorter;
945     }
946
947     return resolved;
948   }
949
950   @NotNull
951   public static List<Pair<PsiMethod, PsiSubstitutor>> findMethodsAndTheirSubstitutorsByName(@NotNull PsiClass psiClass,
952                                                                                             String name,
953                                                                                             boolean checkBases) {
954     if (!checkBases) {
955       final PsiMethod[] methodsByName = psiClass.findMethodsByName(name, false);
956       final List<Pair<PsiMethod, PsiSubstitutor>> ret = new ArrayList<>(methodsByName.length);
957       for (final PsiMethod method : methodsByName) {
958         ret.add(Pair.create(method, PsiSubstitutor.EMPTY));
959       }
960       return ret;
961     }
962     PsiMember[] list = getMap(psiClass, MemberType.METHOD).get(name);
963     if (list == null) return Collections.emptyList();
964     return withSubstitutors(psiClass, list);
965   }
966
967   @NotNull
968   private static <T extends PsiMember> List<Pair<T, PsiSubstitutor>> withSubstitutors(@NotNull final PsiClass psiClass, PsiMember[] members) {
969     final ScopedClassHierarchy hierarchy = ScopedClassHierarchy.getHierarchy(psiClass, psiClass.getResolveScope());
970     final LanguageLevel level = PsiUtil.getLanguageLevel(psiClass);
971     return ContainerUtil.map(members, member -> {
972       PsiClass containingClass = member.getContainingClass();
973       PsiSubstitutor substitutor = containingClass == null ? null : hierarchy.getSuperMembersSubstitutor(containingClass, level);
974       //noinspection unchecked
975       return Pair.create((T)member, substitutor == null ? PsiSubstitutor.EMPTY : substitutor);
976     });
977   }
978
979   @NotNull
980   public static PsiClassType[] getExtendsListTypes(@NotNull PsiClass psiClass) {
981     if (psiClass.isEnum()) {
982       PsiClassType enumSuperType = getEnumSuperType(psiClass, JavaPsiFacade.getElementFactory(psiClass.getProject()));
983       return enumSuperType == null ? PsiClassType.EMPTY_ARRAY : new PsiClassType[]{enumSuperType};
984     }
985     if (psiClass.isAnnotationType()) {
986       return new PsiClassType[]{getAnnotationSuperType(psiClass, JavaPsiFacade.getElementFactory(psiClass.getProject()))};
987     }
988     PsiType upperBound = psiClass instanceof PsiTypeParameter ? TypeConversionUtil.getInferredUpperBoundForSynthetic((PsiTypeParameter)psiClass) : null;
989     if (upperBound == null && psiClass instanceof PsiTypeParameter) {
990       upperBound = LambdaUtil.getFunctionalTypeMap().get(psiClass);
991     }
992     if (upperBound instanceof PsiIntersectionType) {
993       final PsiType[] conjuncts = ((PsiIntersectionType)upperBound).getConjuncts();
994       final List<PsiClassType> result = new ArrayList<>();
995       for (PsiType conjunct : conjuncts) {
996         if (conjunct instanceof PsiClassType) {
997           result.add((PsiClassType)conjunct);
998         }
999       }
1000       return result.toArray(PsiClassType.EMPTY_ARRAY);
1001     }
1002     if (upperBound instanceof PsiClassType) {
1003       return new PsiClassType[] {(PsiClassType)upperBound};
1004     }
1005     final PsiReferenceList extendsList = psiClass.getExtendsList();
1006     if (extendsList != null) {
1007       return extendsList.getReferencedTypes();
1008     }
1009     return PsiClassType.EMPTY_ARRAY;
1010   }
1011
1012   @NotNull
1013   public static PsiClassType[] getImplementsListTypes(@NotNull PsiClass psiClass) {
1014     final PsiReferenceList extendsList = psiClass.getImplementsList();
1015     if (extendsList != null) {
1016       return extendsList.getReferencedTypes();
1017     }
1018     return PsiClassType.EMPTY_ARRAY;
1019   }
1020
1021   static boolean isInExtendsList(@NotNull PsiClass psiClass,
1022                                  @NotNull PsiClass baseClass,
1023                                  @Nullable String baseName,
1024                                  @NotNull PsiManager manager) {
1025     if (psiClass.isEnum()) {
1026       return CommonClassNames.JAVA_LANG_ENUM.equals(baseClass.getQualifiedName());
1027     }
1028     if (psiClass.isAnnotationType()) {
1029       return CommonClassNames.JAVA_LANG_ANNOTATION_ANNOTATION.equals(baseClass.getQualifiedName());
1030     }
1031     PsiType upperBound = psiClass instanceof PsiTypeParameter ? TypeConversionUtil.getInferredUpperBoundForSynthetic((PsiTypeParameter)psiClass) : null;
1032     if (upperBound == null && psiClass instanceof PsiTypeParameter) {
1033       upperBound = LambdaUtil.getFunctionalTypeMap().get(psiClass);
1034     }
1035     if (upperBound instanceof PsiIntersectionType) {
1036       final PsiType[] conjuncts = ((PsiIntersectionType)upperBound).getConjuncts();
1037       for (PsiType conjunct : conjuncts) {
1038         if (conjunct instanceof PsiClassType && ((PsiClassType)conjunct).getClassName().equals(baseName) && baseClass.equals(((PsiClassType)conjunct).resolve())) {
1039           return true;
1040         }
1041       }
1042       return false;
1043     }
1044     if (upperBound instanceof PsiClassType) {
1045       return ((PsiClassType)upperBound).getClassName().equals(baseName) && baseClass.equals(((PsiClassType)upperBound).resolve());
1046     }
1047
1048     return isInReferenceList(psiClass.getExtendsList(), baseClass, baseName, manager);
1049   }
1050
1051   static boolean isInReferenceList(@Nullable PsiReferenceList list,
1052                                    @NotNull PsiClass baseClass,
1053                                    @Nullable String baseName,
1054                                    @NotNull PsiManager manager) {
1055     if (list == null) return false;
1056     if (list instanceof StubBasedPsiElement) {
1057       StubElement stub = ((StubBasedPsiElement)list).getStub();
1058       if (stub instanceof PsiClassReferenceListStub && baseName != null) {
1059         // classStub.getReferencedNames() is cheaper than getReferencedTypes()
1060         PsiClassReferenceListStub classStub = (PsiClassReferenceListStub)stub;
1061         String[] names = classStub.getReferencedNames();
1062         for (int i = 0; i < names.length; i++) {
1063           String name = PsiNameHelper.getShortClassName(names[i]);
1064           // baseName=="ArrayList" classStub.getReferenceNames()[i]=="java.util.ArrayList"
1065           if (name.endsWith(baseName)) {
1066             PsiClassType[] referencedTypes = classStub.getReferencedTypes();
1067             PsiClassType type = i >= referencedTypes.length ? null : referencedTypes[i];
1068             PsiClass resolved = type == null ? null : type.resolve();
1069             if (manager.areElementsEquivalent(baseClass, resolved)) return true;
1070           }
1071         }
1072         return false;
1073       }
1074       if (stub != null) {
1075         // groovy etc
1076         for (PsiClassType type : list.getReferencedTypes()) {
1077           if (Comparing.equal(type.getClassName(), baseName) && manager.areElementsEquivalent(baseClass, type.resolve())) {
1078             return true;
1079           }
1080         }
1081         return false;
1082       }
1083     }
1084
1085     if (list.getLanguage() == JavaLanguage.INSTANCE) {
1086       // groovy doesn't have list.getReferenceElements()
1087       for (PsiJavaCodeReferenceElement referenceElement : list.getReferenceElements()) {
1088         if (Comparing.strEqual(baseName, referenceElement.getReferenceName()) &&
1089             manager.areElementsEquivalent(baseClass, referenceElement.resolve())) {
1090           return true;
1091         }
1092       }
1093       return false;
1094     }
1095
1096     for (PsiClassType type : list.getReferencedTypes()) {
1097       if (Comparing.equal(type.getClassName(), baseName) && manager.areElementsEquivalent(baseClass, type.resolve())) {
1098         return true;
1099       }
1100     }
1101     return false;
1102   }
1103
1104   public static boolean isClassEquivalentTo(@NotNull PsiClass aClass, PsiElement another) {
1105     if (aClass == another) return true;
1106     if (!(another instanceof PsiClass)) return false;
1107     String name1 = aClass.getName();
1108     if (name1 == null) return false;
1109     if (!another.isValid()) return false;
1110     String name2 = ((PsiClass)another).getName();
1111     if (name2 == null) return false;
1112     if (name1.hashCode() != name2.hashCode()) return false;
1113     if (!name1.equals(name2)) return false;
1114     String qName1 = aClass.getQualifiedName();
1115     String qName2 = ((PsiClass)another).getQualifiedName();
1116     if (qName1 == null || qName2 == null) {
1117       //noinspection StringEquality
1118       if (qName1 != qName2) return false;
1119
1120       if (aClass instanceof PsiTypeParameter && another instanceof PsiTypeParameter) {
1121         PsiTypeParameter p1 = (PsiTypeParameter)aClass;
1122         PsiTypeParameter p2 = (PsiTypeParameter)another;
1123
1124         return p1.getIndex() == p2.getIndex() &&
1125                (aClass.getManager().areElementsEquivalent(p1.getOwner(), p2.getOwner()) || TypeConversionUtil.areSameFreshVariables(p1, p2));
1126       }
1127       else {
1128         return false;
1129       }
1130     }
1131     if (qName1.hashCode() != qName2.hashCode() || !qName1.equals(qName2)) {
1132       return false;
1133     }
1134
1135     if (aClass.getOriginalElement().equals(another.getOriginalElement())) {
1136       return true;
1137     }
1138
1139     final PsiFile file1 = getOriginalFile(aClass);
1140     final PsiFile file2 = getOriginalFile((PsiClass)another);
1141
1142     //see com.intellij.openapi.vcs.changes.PsiChangeTracker
1143     //see com.intellij.psi.impl.PsiFileFactoryImpl#createFileFromText(CharSequence,PsiFile)
1144     final PsiFile original1 = file1.getUserData(PsiFileFactory.ORIGINAL_FILE);
1145     final PsiFile original2 = file2.getUserData(PsiFileFactory.ORIGINAL_FILE);
1146     if (original1 == original2 && original1 != null || original1 == file2 || original2 == file1 || file1 == file2) {
1147       return true;
1148     }
1149
1150     final FileIndexFacade fileIndex = ServiceManager.getService(file1.getProject(), FileIndexFacade.class);
1151     final VirtualFile vfile1 = file1.getViewProvider().getVirtualFile();
1152     final VirtualFile vfile2 = file2.getViewProvider().getVirtualFile();
1153     boolean lib1 = fileIndex.isInLibraryClasses(vfile1);
1154     boolean lib2 = fileIndex.isInLibraryClasses(vfile2);
1155
1156     return (fileIndex.isInSource(vfile1) || lib1) && (fileIndex.isInSource(vfile2) || lib2);
1157   }
1158
1159   @NotNull
1160   private static PsiFile getOriginalFile(@NotNull PsiClass aClass) {
1161     PsiFile file = aClass.getContainingFile();
1162     if (file == null) {
1163       PsiUtilCore.ensureValid(aClass);
1164       throw new IllegalStateException("No containing file for " + aClass.getLanguage() + " " + aClass.getClass());
1165     }
1166     return file.getOriginalFile();
1167   }
1168
1169   public static boolean isFieldEquivalentTo(@NotNull PsiField field, PsiElement another) {
1170     if (!(another instanceof PsiField)) return false;
1171     String name1 = field.getName();
1172     if (name1 == null) return false;
1173     if (!another.isValid()) return false;
1174
1175     String name2 = ((PsiField)another).getName();
1176     if (!name1.equals(name2)) return false;
1177     PsiClass aClass1 = field.getContainingClass();
1178     PsiClass aClass2 = ((PsiField)another).getContainingClass();
1179     return aClass1 != null && aClass2 != null && field.getManager().areElementsEquivalent(aClass1, aClass2);
1180   }
1181
1182   public static boolean isMethodEquivalentTo(@NotNull PsiMethod method1, PsiElement another) {
1183     if (method1 == another) return true;
1184     if (!(another instanceof PsiMethod)) return false;
1185     PsiMethod method2 = (PsiMethod)another;
1186     if (!another.isValid()) return false;
1187     if (!method1.getName().equals(method2.getName())) return false;
1188     PsiClass aClass1 = method1.getContainingClass();
1189     PsiClass aClass2 = method2.getContainingClass();
1190     PsiManager manager = method1.getManager();
1191     if (!(aClass1 != null && aClass2 != null && manager.areElementsEquivalent(aClass1, aClass2))) return false;
1192
1193     PsiParameter[] parameters1 = method1.getParameterList().getParameters();
1194     PsiParameter[] parameters2 = method2.getParameterList().getParameters();
1195     if (parameters1.length != parameters2.length) return false;
1196     for (int i = 0; i < parameters1.length; i++) {
1197       PsiParameter parameter1 = parameters1[i];
1198       PsiParameter parameter2 = parameters2[i];
1199       PsiType type1 = parameter1.getType();
1200       PsiType type2 = parameter2.getType();
1201       if (!compareParamTypes(manager, type1, type2, new HashSet<>())) return false;
1202     }
1203     return true;
1204   }
1205
1206   private static boolean compareParamTypes(@NotNull PsiManager manager, @NotNull PsiType type1, @NotNull PsiType type2, Set<? super String> visited) {
1207     if (type1 instanceof PsiArrayType) {
1208       if (type2 instanceof PsiArrayType) {
1209         final PsiType componentType1 = ((PsiArrayType)type1).getComponentType();
1210         final PsiType componentType2 = ((PsiArrayType)type2).getComponentType();
1211         if (compareParamTypes(manager, componentType1, componentType2, visited)) return true;
1212       }
1213       return false;
1214     }
1215
1216     if (!(type1 instanceof PsiClassType) || !(type2 instanceof PsiClassType)) {
1217       return type1.equals(type2);
1218     }
1219
1220     PsiClass class1 = ((PsiClassType)type1).resolve();
1221     PsiClass class2 = ((PsiClassType)type2).resolve();
1222     visited.add(type1.getCanonicalText());
1223     visited.add(type2.getCanonicalText());
1224
1225     if (class1 instanceof PsiTypeParameter && class2 instanceof PsiTypeParameter) {
1226       if (!(Comparing.equal(class1.getName(), class2.getName()) && ((PsiTypeParameter)class1).getIndex() == ((PsiTypeParameter)class2).getIndex())) return false;
1227       final PsiClassType[] eTypes1 = class1.getExtendsListTypes();
1228       final PsiClassType[] eTypes2 = class2.getExtendsListTypes();
1229       if (eTypes1.length != eTypes2.length) return false;
1230       for (int i = 0; i < eTypes1.length; i++) {
1231         PsiClassType eType1 = eTypes1[i];
1232         PsiClassType eType2 = eTypes2[i];
1233         if (visited.contains(eType1.getCanonicalText()) || visited.contains(eType2.getCanonicalText())) {
1234           return false;
1235         }
1236         if (!compareParamTypes(manager, eType1, eType2, visited)) return false;
1237       }
1238       return true;
1239     }
1240
1241     return manager.areElementsEquivalent(class1, class2);
1242   }
1243 }