Files
openide/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java
Tagir Valeev 27e21144a2 [java-psi] IDEA-367231 Incorrect use scope for public class inside non-public non-final containing class
(cherry picked from commit 9238a1c2cb5749f4a711815150c14324d8d7f64e)

IJ-CR-154834

GitOrigin-RevId: ecfa67d0b540e43d1054d2e283ec4e85badc3dae
2025-02-12 14:02:58 +00:00

1236 lines
57 KiB
Java

// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.psi.impl;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicatorProvider;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.roots.FileIndexFacade;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.Strings;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.augment.PsiExtensionMethod;
import com.intellij.psi.impl.java.stubs.PsiClassReferenceListStub;
import com.intellij.psi.impl.java.stubs.PsiClassStub;
import com.intellij.psi.impl.java.stubs.impl.PsiClassStubImpl;
import com.intellij.psi.impl.source.PsiImmediateClassType;
import com.intellij.psi.scope.ElementClassHint;
import com.intellij.psi.scope.NameHint;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.scope.processor.MethodResolverProcessor;
import com.intellij.psi.search.*;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.util.*;
import com.intellij.ui.IconManager;
import com.intellij.ui.icons.RowIcon;
import com.intellij.util.*;
import com.intellij.util.containers.ConcurrentFactoryMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.JBTreeTraverser;
import kotlin.jvm.functions.Function1;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
public final class PsiClassImplUtil {
private static final Logger LOG = Logger.getInstance(PsiClassImplUtil.class);
private PsiClassImplUtil() { }
public static PsiField @NotNull [] getAllFields(@NotNull PsiClass aClass) {
List<PsiField> map = getAllByMap(aClass, MemberType.FIELD);
return map.toArray(PsiField.EMPTY_ARRAY);
}
public static PsiMethod @NotNull [] getAllMethods(@NotNull PsiClass aClass) {
List<PsiMethod> methods = getAllByMap(aClass, MemberType.METHOD);
return methods.toArray(PsiMethod.EMPTY_ARRAY);
}
public static PsiClass @NotNull [] getAllInnerClasses(@NotNull PsiClass aClass) {
List<PsiClass> classes = getAllByMap(aClass, MemberType.CLASS);
return classes.toArray(PsiClass.EMPTY_ARRAY);
}
/**
* @return a list of all direct super classes of {@param aClass} concatenated with their super classes, recursively up to the top
*/
public static @NotNull Collection<PsiClass> getAllSuperClassesRecursively(@NotNull PsiClass aClass) {
return getMap(aClass).myAllSupers;
}
public static @Nullable PsiField findFieldByName(@NotNull PsiClass aClass, String name, boolean checkBases) {
List<PsiMember> byMap = findByMap(aClass, name, checkBases, MemberType.FIELD);
return byMap.isEmpty() ? null : (PsiField)byMap.get(0);
}
public static PsiMethod @NotNull [] findMethodsByName(@NotNull PsiClass aClass, String name, boolean checkBases) {
List<PsiMember> methods = findByMap(aClass, name, checkBases, MemberType.METHOD);
//noinspection SuspiciousToArrayCall
return methods.toArray(PsiMethod.EMPTY_ARRAY);
}
public static @Nullable PsiMethod findMethodBySignature(@NotNull PsiClass aClass, @NotNull PsiMethod patternMethod, boolean checkBases) {
List<PsiMethod> result = findMethodsBySignature(aClass, patternMethod, checkBases, true);
return result.isEmpty() ? null : result.get(0);
}
// ----------------------------- findMethodsBySignature -----------------------------------
public static PsiMethod @NotNull [] findMethodsBySignature(@NotNull PsiClass aClass, @NotNull PsiMethod patternMethod, boolean checkBases) {
List<PsiMethod> methods = findMethodsBySignature(aClass, patternMethod, checkBases, false);
return methods.toArray(PsiMethod.EMPTY_ARRAY);
}
private static @NotNull List<PsiMethod> findMethodsBySignature(@NotNull PsiClass aClass,
@NotNull PsiMethod patternMethod,
boolean checkBases,
boolean stopOnFirst) {
PsiMethod[] methodsByName = aClass.findMethodsByName(patternMethod.getName(), checkBases);
if (methodsByName.length == 0) return Collections.emptyList();
List<PsiMethod> methods = new SmartList<>();
MethodSignature patternSignature = patternMethod.getSignature(PsiSubstitutor.EMPTY);
for (PsiMethod method : methodsByName) {
PsiClass superClass = method.getContainingClass();
PsiSubstitutor substitutor = checkBases && !aClass.equals(superClass) && superClass != null ?
TypeConversionUtil.getSuperClassSubstitutor(superClass, aClass, PsiSubstitutor.EMPTY) :
PsiSubstitutor.EMPTY;
MethodSignature signature = method.getSignature(substitutor);
if (signature.equals(patternSignature)) {
methods.add(method);
if (stopOnFirst) {
break;
}
}
}
return methods;
}
// ----------------------------------------------------------------------------------------
public static @Nullable PsiClass findInnerByName(@NotNull PsiClass aClass, String name, boolean checkBases) {
List<PsiMember> byMap = findByMap(aClass, name, checkBases, MemberType.CLASS);
return byMap.isEmpty() ? null : (PsiClass)byMap.get(0);
}
public static boolean processAllMembersWithoutSubstitutors(@NotNull PsiClass psiClass, @NotNull PsiScopeProcessor processor, @NotNull ResolveState state) {
ElementClassHint classHint = processor.getHint(ElementClassHint.KEY);
NameHint nameHint = processor.getHint(NameHint.KEY);
String name = nameHint == null ? null : nameHint.getName(state);
if ((classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.METHOD)) &&
!processMembers(state, processor, getMap(psiClass).getAllMembers(MemberType.METHOD, name))) {
return false;
}
if ((classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.FIELD)) &&
!processMembers(state, processor, getMap(psiClass).getAllMembers(MemberType.FIELD, name))) {
return false;
}
return (classHint != null && !classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) ||
processMembers(state, processor, getMap(psiClass).getAllMembers(MemberType.CLASS, name));
}
private static boolean processMembers(ResolveState state, PsiScopeProcessor processor, PsiMember @NotNull[] members) {
for (PsiMember member : members) {
if (!processor.execute(member, state)) {
return false;
}
}
return true;
}
private static @NotNull List<PsiMember> findByMap(@NotNull PsiClass aClass, String name, boolean checkBases, @NotNull MemberType type) {
if (name == null) return Collections.emptyList();
return checkBases
? getMap(aClass).calcMembersByName(type, name, null, null)
: ContainerUtil.filter(type.getMembers(aClass), member -> name.equals(member.getName()));
}
public static @NotNull <T extends PsiMember> List<Pair<T, PsiSubstitutor>> getAllWithSubstitutorsByMap(@NotNull PsiClass aClass, @NotNull MemberType type) {
return withSubstitutors(aClass, getMap(aClass).getAllMembers(type, null));
}
private static @NotNull <T extends PsiMember> List<T> getAllByMap(@NotNull PsiClass aClass, @NotNull MemberType type) {
//noinspection unchecked
return Arrays.asList((T[])getMap(aClass).getAllMembers(type, null));
}
public enum MemberType {
CLASS, FIELD, METHOD;
PsiMember @NotNull[] getMembers(@NotNull PsiClass aClass) {
switch (this) {
case METHOD: return aClass.getMethods();
case CLASS: return aClass.getInnerClasses();
default: return aClass.getFields();
}
}
}
private static MemberCache getMap(@NotNull PsiClass aClass) {
return getMap(aClass, aClass.getResolveScope());
}
private static MemberCache getMap(@NotNull PsiClass aClass, @NotNull GlobalSearchScope scope) {
return CachedValuesManager.getProjectPsiDependentCache(aClass, c ->
ConcurrentFactoryMap.createMap((GlobalSearchScope s) -> new MemberCache(c, s))).get(scope);
}
private static final class ClassIconRequest {
private final @NotNull PsiClass psiClass;
private final int flags;
private final Icon symbolIcon;
private ClassIconRequest(@NotNull PsiClass psiClass, int flags, Icon symbolIcon) {
this.psiClass = psiClass;
this.flags = flags;
this.symbolIcon = symbolIcon;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ClassIconRequest)) return false;
ClassIconRequest that = (ClassIconRequest)o;
return flags == that.flags && psiClass.equals(that.psiClass);
}
@Override
public int hashCode() {
int result = psiClass.hashCode();
result = 31 * result + flags;
return result;
}
}
private static final Function1<ClassIconRequest, Icon> FULL_ICON_EVALUATOR = r -> {
if (!r.psiClass.isValid() || r.psiClass.getProject().isDisposed()) {
return null;
}
boolean isLocked = BitUtil.isSet(r.flags, Iconable.ICON_FLAG_READ_STATUS) && !r.psiClass.isWritable();
Icon symbolIcon = r.symbolIcon != null
? r.symbolIcon
: ElementPresentationUtil.getClassIconOfKind(r.psiClass, ElementPresentationUtil.getClassKind(r.psiClass));
RowIcon baseIcon =
IconManager.getInstance().createLayeredIcon(r.psiClass, symbolIcon, ElementPresentationUtil.getFlags(r.psiClass, isLocked));
Icon result = ElementPresentationUtil.addVisibilityIcon(r.psiClass, r.flags, baseIcon);
LastComputedIconCache.put(r.psiClass, result, r.flags);
return result;
};
public static Icon getClassIcon(int flags, @NotNull PsiClass aClass) {
return getClassIcon(flags, aClass, null);
}
public static Icon getClassIcon(int flags, @NotNull PsiClass aClass, @Nullable Icon symbolIcon) {
Icon base = LastComputedIconCache.get(aClass, flags);
if (base == null) {
Icon baseSymbolIcon = symbolIcon;
if (baseSymbolIcon == null) {
baseSymbolIcon = ElementPresentationUtil.getClassIconOfKind(aClass, ElementPresentationUtil.getBasicClassKind(aClass));
}
RowIcon baseIcon = IconManager.getInstance().createLayeredIcon(aClass, baseSymbolIcon, 0);
base = ElementPresentationUtil.addVisibilityIcon(aClass, flags, baseIcon);
}
return IconManager.getInstance().createDeferredIcon(base, new ClassIconRequest(aClass, flags, symbolIcon), FULL_ICON_EVALUATOR);
}
public static @NotNull SearchScope getClassUseScope(@NotNull PsiClass aClass) {
if (aClass instanceof PsiAnonymousClass) {
return new LocalSearchScope(aClass);
}
if (aClass instanceof StubBasedPsiElement) {
StubElement<?> stubElement = ((StubBasedPsiElement<?>)aClass).getStub();
if (stubElement instanceof PsiClassStub) {
PsiClassStub<?> stub = (PsiClassStub<?>)stubElement;
if (stub instanceof PsiClassStubImpl &&
((PsiClassStubImpl<?>)stub).isLocalClassInner()) {
// at least it's smaller than the containing package
return new LocalSearchScope(aClass.getContainingFile());
}
}
}
else {
PsiElement parent = aClass.getParent();
if (parent instanceof PsiDeclarationStatement) {
PsiElement grandParent = parent.getParent();
if (grandParent instanceof PsiCodeBlock) {
return new LocalSearchScope(grandParent);
// Actually: The scope of a local class or interface declaration immediately enclosed by a block is the rest
// of the immediately enclosing block, including the local class or interface declaration itself (jls-6.3).
}
}
}
GlobalSearchScope maximalUseScope = ResolveScopeManager.getElementUseScope(aClass);
PsiFile file = aClass.getContainingFile();
if (PsiImplUtil.isInServerPage(file)) return maximalUseScope;
SearchScope searchScope = PsiSearchScopeUtil.USE_SCOPE_KEY.get(file);
if (searchScope != null) return searchScope;
PsiClass containingClass = aClass.getContainingClass();
if (aClass.hasModifierProperty(PsiModifier.PUBLIC) ||
aClass.hasModifierProperty(PsiModifier.PROTECTED)) {
// If the containing class is not final, it's possible to expose nested class through public subclass of containing class
return containingClass == null || !containingClass.hasModifierProperty(PsiModifier.FINAL) ?
maximalUseScope : containingClass.getUseScope();
}
else if (aClass.hasModifierProperty(PsiModifier.PRIVATE) || aClass instanceof PsiTypeParameter) {
PsiClass topClass = PsiUtil.getTopLevelClass(aClass);
return new LocalSearchScope(topClass == null ? aClass.getContainingFile() : topClass);
}
else {
PsiPackage aPackage = null;
if (file instanceof PsiJavaFile) {
aPackage = JavaPsiFacade.getInstance(aClass.getProject()).findPackage(((PsiJavaFile)file).getPackageName());
}
if (aPackage == null) {
PsiDirectory dir = file.getContainingDirectory();
if (dir != null) {
aPackage = JavaDirectoryService.getInstance().getPackage(dir);
}
}
if (aPackage != null) {
SearchScope scope = PackageScope.packageScope(aPackage, false);
scope = scope.intersectWith(maximalUseScope);
return scope;
}
return new LocalSearchScope(file);
}
}
public static boolean isMainOrPremainMethod(@NotNull PsiMethod method) {
if ("main".equals(method.getName()) && PsiMethodUtil.isMainMethod(method)) {
return true;
}
String name = method.getName();
if (!("main".equals(name) || "premain".equals(name) || "agentmain".equals(name))) return false;
if (!PsiTypes.voidType().equals(method.getReturnType())) return false;
PsiElementFactory factory = JavaPsiFacade.getElementFactory(method.getProject());
MethodSignature signature = method.getSignature(PsiSubstitutor.EMPTY);
try {
MethodSignature main = createSignatureFromText(factory, "void main(String[] args);");
if (MethodSignatureUtil.areSignaturesEqual(signature, main)) return true;
MethodSignature premain = createSignatureFromText(factory, "void premain(String args, java.lang.instrument.Instrumentation i);");
if (MethodSignatureUtil.areSignaturesEqual(signature, premain)) return true;
MethodSignature agentmain = createSignatureFromText(factory, "void agentmain(String args, java.lang.instrument.Instrumentation i);");
if (MethodSignatureUtil.areSignaturesEqual(signature, agentmain)) return true;
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
return false;
}
private static @NotNull MethodSignature createSignatureFromText(@NotNull PsiElementFactory factory, @NotNull String text) {
return factory.createMethodFromText(text, null).getSignature(PsiSubstitutor.EMPTY);
}
private static class MemberCache {
private final @NotNull List<PsiClass> myAllSupers;
private final ConcurrentMap<MemberType, PsiMember[]> myAllMembers;
MemberCache(@NotNull PsiClass psiClass, @NotNull GlobalSearchScope scope) {
boolean dumb = DumbService.isDumb(psiClass.getProject());
myAllSupers = JBTreeTraverser
.from((PsiClass c) -> ContainerUtil.mapNotNull(c.getSupers(),
superOne -> {
PsiClass correctedClass = PsiSuperMethodUtil.correctClassByScope(superOne, scope);
if (correctedClass == null && dumb) {
if (superOne.getContainingFile() instanceof PsiJavaFile &&
((PsiJavaFile)superOne.getContainingFile()).getPackageStatement() == null) {
return superOne;
}
}
return correctedClass;
}))
.unique()
.withRoot(psiClass)
.toList();
myAllMembers = ConcurrentFactoryMap.createMap(
type -> StreamEx.of(myAllSupers).flatArray(type::getMembers).filter(e -> !skipInvalid(e)).toArray(PsiMember.EMPTY_ARRAY));
}
@NotNull List<PsiMember> calcMembersByName(@NotNull MemberType type, @NotNull String name, PsiClass psiClass, PsiElement context) {
List<PsiMember> result = null;
for (PsiClass eachSuper : myAllSupers) {
if (type == MemberType.METHOD) {
PsiMethod[] methods = eachSuper.findMethodsByName(name, false);
if (methods.length > 0) {
if (result == null) result = new ArrayList<>();
Collections.addAll(result, methods);
}
}
else {
PsiMember member = type == MemberType.CLASS
? eachSuper.findInnerClassByName(name, false)
: eachSuper.findFieldByName(name, false);
if (member != null) {
if (result == null) result = new ArrayList<>();
result.add(member);
}
}
}
if (type == MemberType.METHOD && psiClass != null && context != null) {
List<PsiExtensionMethod> methods = PsiAugmentProvider.collectExtensionMethods(psiClass, name, context);
if (!methods.isEmpty()) {
if (result == null) result = new ArrayList<>();
result.addAll(methods);
}
}
return result == null ? Collections.emptyList() : ContainerUtil.filter(result, e -> !skipInvalid(e));
}
PsiMember[] getAllMembers(@NotNull MemberType type, @Nullable String name) {
return name == null ? myAllMembers.get(type) : calcMembersByName(type, name, null, null).toArray(PsiMember.EMPTY_ARRAY);
}
}
private static boolean skipInvalid(@NotNull PsiElement element) {
try {
PsiUtilCore.ensureValid(element);
}
catch (ProcessCanceledException e) {
throw e;
}
catch (Throwable e) {
LOG.error(e);
return true;
}
return false;
}
public static boolean processDeclarationsInClass(@NotNull PsiClass aClass,
@NotNull PsiScopeProcessor processor,
@NotNull ResolveState state,
@Nullable Set<PsiClass> visited,
PsiElement last,
@NotNull PsiElement place,
@NotNull LanguageLevel languageLevel,
boolean isRaw) {
return processDeclarationsInClass(aClass, processor, state, visited, last, place, languageLevel, isRaw, place.getResolveScope());
}
private static boolean processDeclarationsInClass(@NotNull PsiClass aClass,
@NotNull PsiScopeProcessor processor,
@NotNull ResolveState state,
@Nullable Set<PsiClass> visited,
PsiElement last,
@NotNull PsiElement place,
@NotNull LanguageLevel languageLevel,
boolean isRaw,
@NotNull GlobalSearchScope resolveScope) {
if (last instanceof PsiTypeParameterList || last instanceof PsiModifierList && aClass.getModifierList() == last) {
return true;
}
if (visited != null && visited.contains(aClass)) return true;
PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY);
isRaw = isRaw || PsiUtil.isRawSubstitutor(aClass, substitutor);
NameHint nameHint = processor.getHint(NameHint.KEY);
String name = nameHint == null ? null : nameHint.getName(state);
if (name != null) {
return processCachedMembersByName(aClass, processor, state, visited, last, place, isRaw, substitutor,
name, languageLevel, resolveScope);
}
return processClassMembersWithAllNames(aClass, processor, state, visited, last, place, isRaw, languageLevel, resolveScope);
}
private static boolean processCachedMembersByName(@NotNull PsiClass aClass,
@NotNull PsiScopeProcessor processor,
@NotNull ResolveState state,
@Nullable Set<? super PsiClass> visited,
PsiElement last,
@NotNull PsiElement place,
boolean isRaw,
@NotNull PsiSubstitutor substitutor,
String name,
@NotNull LanguageLevel languageLevel,
@NotNull GlobalSearchScope resolveScope) {
java.util.function.Function<PsiMember, PsiSubstitutor> finalSubstitutor = new java.util.function.Function<PsiMember, PsiSubstitutor>() {
final ScopedClassHierarchy hierarchy = ScopedClassHierarchy.getHierarchy(aClass, resolveScope);
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(aClass.getProject());
@Override
public PsiSubstitutor apply(PsiMember member) {
PsiSubstitutor finalSubstitutor = member.hasModifierProperty(PsiModifier.STATIC) ? substitutor : obtainSubstitutor(member);
return member instanceof PsiMethod ? checkRaw(isRaw, factory, (PsiMethod)member, finalSubstitutor) : finalSubstitutor;
}
private PsiSubstitutor obtainSubstitutor(PsiMember member) {
PsiClass containingClass = Objects.requireNonNull(member.getContainingClass());
PsiSubstitutor superSubstitutor = ObjectUtils.notNull(hierarchy.getSuperMembersSubstitutor(containingClass, languageLevel),
PsiSubstitutor.EMPTY);
return obtainFinalSubstitutor(containingClass, superSubstitutor, aClass, substitutor, factory, languageLevel);
}
};
ElementClassHint classHint = processor.getHint(ElementClassHint.KEY);
if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.FIELD)) {
PsiField fieldByName = aClass.findFieldByName(name, false);
if (fieldByName != null) {
if (!skipInvalid(fieldByName)) {
processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
if (!processor.execute(fieldByName, state)) return false;
}
}
else {
List<PsiMember> list = getMap(aClass, resolveScope).calcMembersByName(MemberType.FIELD, name, aClass, place);
if (!list.isEmpty()) {
boolean resolved = false;
for (PsiMember candidateField : list) {
PsiClass containingClass = candidateField.getContainingClass();
if (skipInvalid(candidateField)) {
continue;
}
if (containingClass == null) {
PsiElement parent = candidateField.getParent();
LOG.error("No class for field " + candidateField.getName() + " of " + candidateField.getClass() +
", parent " + parent + " of " + (parent == null ? null : parent.getClass()));
continue;
}
processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, containingClass);
if (!processor.execute(candidateField, state.put(PsiSubstitutor.KEY, finalSubstitutor.apply(candidateField)))) {
resolved = true;
}
}
if (resolved) return false;
}
}
}
if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) {
if (last != null && last.getContext() == aClass) {
if (last instanceof PsiClass) {
if (!processor.execute(last, state)) return false;
}
// Parameters
PsiTypeParameterList list = aClass.getTypeParameterList();
if (list != null && !list.processDeclarations(processor, state, last, place)) return false;
}
if (!(last instanceof PsiReferenceList)) {
PsiClass classByName = aClass.findInnerClassByName(name, false);
if (classByName != null) {
if (!skipInvalid(classByName)) {
processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
if (!processor.execute(classByName, state)) return false;
}
}
else {
List<PsiMember> list = getMap(aClass, resolveScope).calcMembersByName(MemberType.CLASS, name, aClass, place);
if (!list.isEmpty()) {
boolean resolved = false;
for (PsiMember inner : list) {
if (skipInvalid(inner)) continue;
PsiClass containingClass = inner.getContainingClass();
if (containingClass != null) {
processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, containingClass);
if (!processor.execute(inner, state.put(PsiSubstitutor.KEY, finalSubstitutor.apply(inner)))) {
resolved = true;
}
}
}
if (resolved) return false;
}
}
}
}
if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.METHOD)) {
if (processor instanceof MethodResolverProcessor) {
MethodResolverProcessor methodResolverProcessor = (MethodResolverProcessor)processor;
if (methodResolverProcessor.isConstructor()) {
PsiMethod[] constructors = aClass.getConstructors();
methodResolverProcessor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
for (PsiMethod constructor : constructors) {
if (!skipInvalid(constructor) && !methodResolverProcessor.execute(constructor, state)) return false;
}
return true;
}
}
List<PsiMember> list = getMap(aClass, resolveScope).calcMembersByName(MemberType.METHOD, name, aClass, place);
if (!list.isEmpty()) {
boolean resolved = false;
for (PsiMember candidate : list) {
ProgressIndicatorProvider.checkCanceled();
PsiMethod candidateMethod = (PsiMethod)candidate;
if (skipInvalid(candidateMethod)) continue;
if (processor instanceof MethodResolverProcessor) {
if (candidateMethod.isConstructor() != ((MethodResolverProcessor)processor).isConstructor()) continue;
}
PsiClass containingClass = candidateMethod.getContainingClass();
if (containingClass == null || visited != null && visited.contains(containingClass)) {
continue;
}
processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, containingClass);
if (!processor.execute(candidateMethod, state.put(PsiSubstitutor.KEY, finalSubstitutor.apply(candidateMethod)))) {
resolved = true;
}
}
if (resolved) return false;
if (visited != null) {
for (PsiMember aList : list) {
visited.add(aList.getContainingClass());
}
}
}
}
return true;
}
private static PsiSubstitutor checkRaw(boolean isRaw,
@NotNull PsiElementFactory factory,
@NotNull PsiMethod candidateMethod,
@NotNull PsiSubstitutor substitutor) {
//4.8-2. Raw Types and Inheritance
//certain members of a raw type are not erased,
//namely static members whose types are parameterized, and members inherited from a non-generic supertype.
if (isRaw && !candidateMethod.hasModifierProperty(PsiModifier.STATIC)) {
PsiClass containingClass = candidateMethod.getContainingClass();
if (containingClass != null && containingClass.hasTypeParameters()) {
PsiTypeParameter[] methodTypeParameters = candidateMethod.getTypeParameters();
substitutor = factory.createRawSubstitutor(substitutor, methodTypeParameters);
}
}
return substitutor;
}
public static PsiSubstitutor obtainFinalSubstitutor(@NotNull PsiClass candidateClass,
@NotNull PsiSubstitutor candidateSubstitutor,
@NotNull PsiClass aClass,
@NotNull PsiSubstitutor substitutor,
@NotNull PsiElementFactory elementFactory,
@NotNull LanguageLevel languageLevel) {
if (PsiUtil.isRawSubstitutor(aClass, substitutor)) {
return elementFactory.createRawSubstitutor(candidateClass).putAll(substitutor);
}
PsiType containingType = elementFactory.createType(candidateClass, candidateSubstitutor, languageLevel);
PsiType type = substitutor.substitute(containingType);
if (!(type instanceof PsiClassType)) return candidateSubstitutor;
return ((PsiClassType)type).resolveGenerics().getSubstitutor();
}
private static boolean processClassMembersWithAllNames(@NotNull PsiClass aClass,
@NotNull PsiScopeProcessor processor,
@NotNull ResolveState state,
@Nullable Set<PsiClass> visited,
PsiElement last,
@NotNull PsiElement place,
boolean isRaw,
@NotNull LanguageLevel languageLevel,
@NotNull GlobalSearchScope resolveScope) {
ProgressManager.checkCanceled();
if (visited == null) {
visited = new HashSet<>();
}
if (!visited.add(aClass)) {
return true;
}
processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, aClass);
ElementClassHint classHint = processor.getHint(ElementClassHint.KEY);
if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.FIELD)) {
if (!processMembers(state, processor, aClass.getFields())) return false;
}
PsiElementFactory factory = JavaPsiFacade.getElementFactory(aClass.getProject());
if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.METHOD)) {
PsiSubstitutor baseSubstitutor = state.get(PsiSubstitutor.KEY);
for (PsiMethod method : aClass.getMethods()) {
PsiSubstitutor finalSubstitutor = checkRaw(isRaw, factory, method, baseSubstitutor);
ResolveState methodState = finalSubstitutor == baseSubstitutor ? state : state.put(PsiSubstitutor.KEY, finalSubstitutor);
if (!processor.execute(method, methodState)) return false;
}
}
if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) {
if (last != null && last.getContext() == aClass) {
// Parameters
PsiTypeParameterList list = aClass.getTypeParameterList();
if (list != null && !list.processDeclarations(processor, ResolveState.initial(), last, place)) return false;
}
if (!(last instanceof PsiReferenceList) && !(last instanceof PsiModifierList)) {
if (!processMembers(state, processor, aClass.getInnerClasses())) return false;
}
}
if (last instanceof PsiReferenceList) return true;
Set<PsiClass> visited1 = visited;
return processSuperTypes(aClass, state.get(PsiSubstitutor.KEY), factory, languageLevel, resolveScope,
(superClass, finalSubstitutor) -> processDeclarationsInClass(superClass, processor, state.put(PsiSubstitutor.KEY, finalSubstitutor), visited1, last, place,
languageLevel, isRaw, resolveScope));
}
public static @Nullable <T extends PsiType> T correctType(@Nullable T originalType, @NotNull GlobalSearchScope resolveScope) {
if (originalType == null || !Registry.is("java.correct.class.type.by.place.resolve.scope", true)) {
return originalType;
}
return new TypeCorrector(resolveScope).correctType(originalType);
}
public static List<PsiClassType.ClassResolveResult> getScopeCorrectedSuperTypes(PsiClass aClass, GlobalSearchScope resolveScope) {
if (skipInvalid(aClass)) {
return Collections.emptyList();
}
return ScopedClassHierarchy.getHierarchy(aClass, resolveScope).getImmediateSupersWithCapturing();
}
static boolean processSuperTypes(@NotNull PsiClass aClass,
PsiSubstitutor substitutor,
@NotNull PsiElementFactory factory,
@NotNull LanguageLevel languageLevel,
GlobalSearchScope resolveScope,
PairProcessor<? super PsiClass, ? super PsiSubstitutor> processor) {
boolean resolved = false;
for (PsiClassType.ClassResolveResult superTypeResolveResult : getScopeCorrectedSuperTypes(aClass, resolveScope)) {
PsiClass superClass = superTypeResolveResult.getElement();
assert superClass != null;
PsiSubstitutor finalSubstitutor = obtainFinalSubstitutor(superClass, superTypeResolveResult.getSubstitutor(), aClass,
substitutor, factory, languageLevel);
if (!processor.process(superClass, finalSubstitutor)) {
resolved = true;
}
}
return !resolved;
}
public static @Nullable PsiClass getSuperClass(@NotNull PsiClass psiClass) {
if (psiClass.isInterface()) {
return findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_OBJECT);
}
if (psiClass.isEnum()) {
return findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_ENUM);
}
if (psiClass.isRecord()) {
return findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_RECORD);
}
if (psiClass instanceof PsiAnonymousClass) {
PsiClassType baseClassReference = ((PsiAnonymousClass)psiClass).getBaseClassType();
PsiClass baseClass = baseClassReference.resolve();
if (baseClass == null || baseClass.isInterface()) return findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_OBJECT);
return baseClass;
}
if (CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass.getQualifiedName())) return null;
PsiClassType[] referenceElements = psiClass.getExtendsListTypes();
if (referenceElements.length == 0) return findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_OBJECT);
PsiClass psiResolved = referenceElements[0].resolve();
return psiResolved == null ? findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_OBJECT) : psiResolved;
}
private static @Nullable PsiClass findSpecialSuperClass(@NotNull PsiClass psiClass, @NotNull String className) {
return JavaPsiFacade.getInstance(psiClass.getProject()).findClass(className, psiClass.getResolveScope());
}
public static PsiClass @NotNull [] getSupers(@NotNull PsiClass psiClass) {
PsiClass[] supers = getSupersInner(psiClass);
for (PsiClass aSuper : supers) {
LOG.assertTrue(aSuper != null);
}
return supers;
}
private static PsiClass @NotNull [] getSupersInner(@NotNull PsiClass psiClass) {
PsiClassType[] extendsListTypes = psiClass.getExtendsListTypes();
if (psiClass.isInterface()) {
return resolveClassReferenceList(extendsListTypes, psiClass, true);
}
if (psiClass instanceof PsiAnonymousClass) {
PsiAnonymousClass psiAnonymousClass = (PsiAnonymousClass)psiClass;
PsiClassType baseClassReference = psiAnonymousClass.getBaseClassType();
PsiClass baseClass = baseClassReference.resolve();
if (baseClass != null) {
if (baseClass.isInterface()) {
PsiClass objectClass = findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_OBJECT);
return objectClass != null ? new PsiClass[]{objectClass, baseClass} : new PsiClass[]{baseClass};
}
return new PsiClass[]{baseClass};
}
PsiClass objectClass = findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_OBJECT);
return objectClass != null ? new PsiClass[]{objectClass} : PsiClass.EMPTY_ARRAY;
}
if (psiClass instanceof PsiTypeParameter) {
if (extendsListTypes.length == 0) {
PsiClass objectClass = findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_OBJECT);
return objectClass != null ? new PsiClass[]{objectClass} : PsiClass.EMPTY_ARRAY;
}
return resolveClassReferenceList(extendsListTypes, psiClass, false);
}
PsiClassType[] implementsListTypes = psiClass.getImplementsListTypes();
PsiClass[] interfaces = resolveClassReferenceList(implementsListTypes, psiClass, false);
PsiClass superClass = getSuperClass(psiClass);
if (superClass == null) return interfaces;
PsiClass[] types = new PsiClass[interfaces.length + 1];
types[0] = superClass;
System.arraycopy(interfaces, 0, types, 1, interfaces.length);
return types;
}
public static PsiClassType @NotNull [] getSuperTypes(@NotNull PsiClass psiClass) {
if (psiClass instanceof PsiAnonymousClass) {
PsiClassType baseClassType = ((PsiAnonymousClass)psiClass).getBaseClassType();
PsiClass baseClass = baseClassType.resolve();
if (baseClass == null || !baseClass.isInterface()) {
return new PsiClassType[]{baseClassType};
}
else {
PsiClassType objectType = PsiType.getJavaLangObject(psiClass.getManager(), psiClass.getResolveScope());
return new PsiClassType[]{objectType, baseClassType};
}
}
PsiClassType[] extendsTypes = psiClass.getExtendsListTypes();
PsiClassType[] implementsTypes = psiClass.getImplementsListTypes();
boolean hasExtends = extendsTypes.length != 0;
int extendsListLength = extendsTypes.length + (hasExtends ? 0 : 1);
PsiClassType[] result = new PsiClassType[extendsListLength + implementsTypes.length];
System.arraycopy(extendsTypes, 0, result, 0, extendsTypes.length);
if (!hasExtends) {
if (CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass.getQualifiedName())) {
return PsiClassType.EMPTY_ARRAY;
}
PsiManager manager = psiClass.getManager();
PsiClassType objectType = PsiType.getJavaLangObject(manager, psiClass.getResolveScope());
result[0] = objectType;
}
System.arraycopy(implementsTypes, 0, result, extendsListLength, implementsTypes.length);
return result;
}
private static @NotNull PsiClassType getAnnotationSuperType(@NotNull PsiClass psiClass, @NotNull PsiElementFactory factory) {
return factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_ANNOTATION_ANNOTATION, psiClass.getResolveScope());
}
private static PsiClassType getEnumSuperType(@NotNull PsiClass psiClass, @NotNull PsiElementFactory factory) {
PsiClassType superType;
PsiClass enumClass = findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_ENUM);
if (enumClass == null) {
try {
superType = (PsiClassType)factory.createTypeFromText(CommonClassNames.JAVA_LANG_ENUM, null);
}
catch (IncorrectOperationException e) {
superType = null;
}
}
else {
PsiTypeParameter[] typeParameters = enumClass.getTypeParameters();
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
if (typeParameters.length == 1) {
substitutor = substitutor.put(typeParameters[0], factory.createType(psiClass));
}
superType = new PsiImmediateClassType(enumClass, substitutor);
}
return superType;
}
public static PsiClass @NotNull [] getInterfaces(@NotNull PsiTypeParameter typeParameter) {
PsiClassType[] referencedTypes = typeParameter.getExtendsListTypes();
if (referencedTypes.length == 0) {
return PsiClass.EMPTY_ARRAY;
}
List<PsiClass> result = new ArrayList<>(referencedTypes.length);
for (PsiClassType referencedType : referencedTypes) {
PsiClass psiClass = referencedType.resolve();
if (psiClass != null && psiClass.isInterface()) {
result.add(psiClass);
}
}
return result.toArray(PsiClass.EMPTY_ARRAY);
}
public static PsiClass @NotNull [] getInterfaces(@NotNull PsiClass psiClass) {
if (psiClass.isInterface()) {
return resolveClassReferenceList(psiClass.getExtendsListTypes(), psiClass, false);
}
if (psiClass instanceof PsiAnonymousClass) {
PsiClassType baseClassReference = ((PsiAnonymousClass)psiClass).getBaseClassType();
PsiClass baseClass = baseClassReference.resolve();
return baseClass != null && baseClass.isInterface() ? new PsiClass[]{baseClass} : PsiClass.EMPTY_ARRAY;
}
PsiClassType[] implementsListTypes = psiClass.getImplementsListTypes();
return resolveClassReferenceList(implementsListTypes, psiClass, false);
}
private static PsiClass @NotNull [] resolveClassReferenceList(PsiClassType @NotNull [] listOfTypes,
@NotNull PsiClass psiClass,
boolean includeObject) {
PsiClass objectClass = null;
if (includeObject) {
objectClass = findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_OBJECT);
if (objectClass == null) includeObject = false;
}
if (listOfTypes.length == 0) {
if (includeObject) return new PsiClass[]{objectClass};
return PsiClass.EMPTY_ARRAY;
}
int referenceCount = listOfTypes.length;
if (includeObject) referenceCount++;
PsiClass[] resolved = new PsiClass[referenceCount];
int resolvedCount = 0;
if (includeObject) resolved[resolvedCount++] = objectClass;
for (PsiClassType reference : listOfTypes) {
PsiClass refResolved = reference.resolve();
if (refResolved != null) resolved[resolvedCount++] = refResolved;
}
if (resolvedCount < referenceCount) {
resolved = ArrayUtil.realloc(resolved, resolvedCount,PsiClass.ARRAY_FACTORY);
}
return resolved;
}
public static @NotNull List<Pair<PsiMethod, PsiSubstitutor>> findMethodsAndTheirSubstitutorsByName(@NotNull PsiClass psiClass,
@NotNull String name,
boolean checkBases) {
if (!checkBases) {
PsiMethod[] methodsByName = psiClass.findMethodsByName(name, false);
List<Pair<PsiMethod, PsiSubstitutor>> ret = new ArrayList<>(methodsByName.length);
for (PsiMethod method : methodsByName) {
ret.add(Pair.create(method, PsiSubstitutor.EMPTY));
}
return ret;
}
List<PsiMember> list = getMap(psiClass).calcMembersByName(MemberType.METHOD, name, null, null);
if (list.isEmpty()) return Collections.emptyList();
return withSubstitutors(psiClass, list.toArray(PsiMember.EMPTY_ARRAY));
}
private static @NotNull <T extends PsiMember> List<Pair<T, PsiSubstitutor>> withSubstitutors(@NotNull PsiClass psiClass, PsiMember[] members) {
ScopedClassHierarchy hierarchy = ScopedClassHierarchy.getHierarchy(psiClass, psiClass.getResolveScope());
LanguageLevel level = PsiUtil.getLanguageLevel(psiClass);
return ContainerUtil.map(members, member -> {
PsiClass containingClass = member.getContainingClass();
PsiSubstitutor substitutor = containingClass == null ? null : hierarchy.getSuperMembersSubstitutor(containingClass, level);
//noinspection unchecked
return Pair.create((T)member, substitutor == null ? PsiSubstitutor.EMPTY : substitutor);
});
}
public static PsiClassType @NotNull [] getExtendsListTypes(@NotNull PsiClass psiClass) {
if (psiClass.isEnum()) {
PsiClassType enumSuperType = getEnumSuperType(psiClass, JavaPsiFacade.getElementFactory(psiClass.getProject()));
return enumSuperType == null ? PsiClassType.EMPTY_ARRAY : new PsiClassType[]{enumSuperType};
}
if (psiClass.isRecord()) {
PsiClass recordClass = findSpecialSuperClass(psiClass, CommonClassNames.JAVA_LANG_RECORD);
if (recordClass != null) {
return new PsiClassType[]{new PsiImmediateClassType(recordClass, PsiSubstitutor.EMPTY)};
}
PsiElementFactory factory = JavaPsiFacade.getElementFactory(psiClass.getProject());
return new PsiClassType[]{factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_RECORD, psiClass.getResolveScope())};
}
if (psiClass.isAnnotationType()) {
return new PsiClassType[]{getAnnotationSuperType(psiClass, JavaPsiFacade.getElementFactory(psiClass.getProject()))};
}
PsiType upperBound = psiClass instanceof PsiTypeParameter ? TypeConversionUtil.getInferredUpperBoundForSynthetic((PsiTypeParameter)psiClass) : null;
if (upperBound == null && psiClass instanceof PsiTypeParameter) {
upperBound = ThreadLocalTypes.getElementType(psiClass);
}
if (upperBound instanceof PsiIntersectionType) {
PsiType[] conjuncts = ((PsiIntersectionType)upperBound).getConjuncts();
List<PsiClassType> result = new ArrayList<>();
for (PsiType conjunct : conjuncts) {
if (conjunct instanceof PsiClassType) {
result.add((PsiClassType)conjunct);
}
}
return result.toArray(PsiClassType.EMPTY_ARRAY);
}
if (upperBound instanceof PsiClassType) {
return new PsiClassType[] {(PsiClassType)upperBound};
}
PsiReferenceList extendsList = psiClass.getExtendsList();
if (extendsList != null) {
return extendsList.getReferencedTypes();
}
return PsiClassType.EMPTY_ARRAY;
}
public static PsiClassType @NotNull [] getImplementsListTypes(@NotNull PsiClass psiClass) {
PsiReferenceList extendsList = psiClass.getImplementsList();
if (extendsList != null) {
return extendsList.getReferencedTypes();
}
return PsiClassType.EMPTY_ARRAY;
}
static boolean isInExtendsList(@NotNull PsiClass psiClass,
@NotNull PsiClass baseClass,
@Nullable String baseName,
@NotNull PsiManager manager) {
if (psiClass.isEnum()) {
return CommonClassNames.JAVA_LANG_ENUM.equals(baseClass.getQualifiedName());
}
if (psiClass.isAnnotationType()) {
return CommonClassNames.JAVA_LANG_ANNOTATION_ANNOTATION.equals(baseClass.getQualifiedName());
}
PsiType upperBound = psiClass instanceof PsiTypeParameter ? TypeConversionUtil.getInferredUpperBoundForSynthetic((PsiTypeParameter)psiClass) : null;
if (upperBound == null && psiClass instanceof PsiTypeParameter) {
upperBound = ThreadLocalTypes.getElementType(psiClass);
}
if (upperBound instanceof PsiIntersectionType) {
PsiType[] conjuncts = ((PsiIntersectionType)upperBound).getConjuncts();
for (PsiType conjunct : conjuncts) {
if (conjunct instanceof PsiClassType && ((PsiClassType)conjunct).getClassName().equals(baseName) && baseClass.equals(((PsiClassType)conjunct).resolve())) {
return true;
}
}
return false;
}
if (upperBound instanceof PsiClassType) {
return ((PsiClassType)upperBound).getClassName().equals(baseName) && baseClass.equals(((PsiClassType)upperBound).resolve());
}
return isInReferenceList(psiClass.getExtendsList(), baseClass, baseName, manager);
}
static boolean isInReferenceList(@Nullable PsiReferenceList list,
@NotNull PsiClass baseClass,
@Nullable String baseName,
@NotNull PsiManager manager) {
if (list == null) return false;
if (list instanceof StubBasedPsiElement) {
StubElement<?> stub = ((StubBasedPsiElement<?>)list).getStub();
if (stub instanceof PsiClassReferenceListStub && baseName != null) {
// classStub.getReferencedNames() is cheaper than getReferencedTypes()
PsiClassReferenceListStub classStub = (PsiClassReferenceListStub)stub;
String[] names = classStub.getReferencedNames();
for (int i = 0; i < names.length; i++) {
String name = PsiNameHelper.getShortClassName(names[i]);
// baseName=="ArrayList" classStub.getReferenceNames()[i]=="java.util.ArrayList"
if (name.endsWith(baseName)) {
PsiClassType[] referencedTypes = classStub.getReferencedTypes();
PsiClassType type = i >= referencedTypes.length ? null : referencedTypes[i];
PsiClass resolved = type == null ? null : type.resolve();
if (manager.areElementsEquivalent(baseClass, resolved)) return true;
}
}
return false;
}
if (stub != null) {
// groovy etc
for (PsiClassType type : list.getReferencedTypes()) {
if (Objects.equals(type.getClassName(), baseName) && manager.areElementsEquivalent(baseClass, type.resolve())) {
return true;
}
}
return false;
}
}
if (list.getLanguage() == JavaLanguage.INSTANCE) {
// groovy doesn't have list.getReferenceElements()
for (PsiJavaCodeReferenceElement referenceElement : list.getReferenceElements()) {
if (Comparing.strEqual(baseName, referenceElement.getReferenceName()) &&
manager.areElementsEquivalent(baseClass, referenceElement.resolve())) {
return true;
}
}
return false;
}
for (PsiClassType type : list.getReferencedTypes()) {
if (Objects.equals(type.getClassName(), baseName) && manager.areElementsEquivalent(baseClass, type.resolve())) {
return true;
}
}
return false;
}
public static boolean isClassEquivalentTo(@NotNull PsiClass aClass, PsiElement another) {
if (aClass == another) return true;
if (!(another instanceof PsiClass)) return false;
if (!another.isValid()) return false;
boolean isImplicitClass = aClass instanceof PsiImplicitClass;
boolean anotherImplicitClass = another instanceof PsiImplicitClass;
if (isImplicitClass != anotherImplicitClass) return false;
if (!isImplicitClass) {
String name1 = aClass.getName();
if (name1 == null) return false;
String name2 = ((PsiClass)another).getName();
if (name2 == null) return false;
if (name1.hashCode() != name2.hashCode()) return false;
if (!name1.equals(name2)) return false;
}
String qName1 = aClass.getQualifiedName();
String qName2 = ((PsiClass)another).getQualifiedName();
if (qName1 == null || qName2 == null) {
if (!Strings.areSameInstance(qName1, qName2)) return false;
if (aClass instanceof PsiTypeParameter && another instanceof PsiTypeParameter) {
PsiTypeParameter p1 = (PsiTypeParameter)aClass;
PsiTypeParameter p2 = (PsiTypeParameter)another;
if (p1.getIndex() != p2.getIndex()) {
return false;
}
if (TypeConversionUtil.areSameFreshVariables(p1, p2)) {
return true;
}
return !Boolean.FALSE.equals(RecursionManager.doPreventingRecursion(Pair.create(p1, p2), true, () ->
aClass.getManager().areElementsEquivalent(p1.getOwner(), p2.getOwner())));
}
else {
return false;
}
}
if (!qName1.equals(qName2)) {
return false;
}
if (aClass.getOriginalElement().equals(another.getOriginalElement())) {
return true;
}
PsiFile file1 = getOriginalFile(aClass);
PsiFile file2 = getOriginalFile((PsiClass)another);
//see com.intellij.openapi.vcs.changes.PsiChangeTracker
//see com.intellij.psi.impl.PsiFileFactoryImpl#createFileFromText(CharSequence,PsiFile)
PsiFile original1 = file1.getUserData(PsiFileFactory.ORIGINAL_FILE);
PsiFile original2 = file2.getUserData(PsiFileFactory.ORIGINAL_FILE);
if (original1 == original2 && original1 != null || original1 == file2 || original2 == file1 || file1 == file2) {
return true;
}
FileIndexFacade fileIndex = FileIndexFacade.getInstance(file1.getProject());
FileIndexFacade fileIndex2 = FileIndexFacade.getInstance(file2.getProject());
VirtualFile vfile1 = file1.getViewProvider().getVirtualFile();
VirtualFile vfile2 = file2.getViewProvider().getVirtualFile();
boolean lib1 = fileIndex.isInLibraryClasses(vfile1);
boolean lib2 = fileIndex2.isInLibraryClasses(vfile2);
//if copy from another project, fileIndex of correct project should be requested
return (fileIndex.isInSource(vfile1) || lib1) && (fileIndex2.isInSource(vfile2) || lib2);
}
private static @NotNull PsiFile getOriginalFile(@NotNull PsiClass aClass) {
PsiFile file = aClass.getContainingFile();
if (file == null) {
PsiUtilCore.ensureValid(aClass);
throw new IllegalStateException("No containing file for " + aClass.getLanguage() + " " + aClass.getClass());
}
return file.getOriginalFile();
}
public static boolean isFieldEquivalentTo(@NotNull PsiField field, PsiElement another) {
if (!(another instanceof PsiField)) return false;
String name1 = field.getName();
if (!another.isValid()) return false;
String name2 = ((PsiField)another).getName();
if (!name1.equals(name2)) return false;
PsiClass aClass1 = field.getContainingClass();
PsiClass aClass2 = ((PsiField)another).getContainingClass();
return aClass1 != null && aClass2 != null && field.getManager().areElementsEquivalent(aClass1, aClass2);
}
public static boolean isMethodEquivalentTo(@NotNull PsiMethod method1, PsiElement another) {
if (method1 == another) return true;
if (!(another instanceof PsiMethod)) return false;
PsiMethod method2 = (PsiMethod)another;
if (!another.isValid()) return false;
if (!method1.getName().equals(method2.getName())) return false;
PsiClass aClass1 = method1.getContainingClass();
PsiClass aClass2 = method2.getContainingClass();
PsiManager manager = method1.getManager();
if (!(aClass1 != null && aClass2 != null && manager.areElementsEquivalent(aClass1, aClass2))) return false;
PsiParameter[] parameters1 = method1.getParameterList().getParameters();
PsiParameter[] parameters2 = method2.getParameterList().getParameters();
if (parameters1.length != parameters2.length) return false;
for (int i = 0; i < parameters1.length; i++) {
PsiParameter parameter1 = parameters1[i];
PsiParameter parameter2 = parameters2[i];
PsiType type1 = parameter1.getType();
PsiType type2 = parameter2.getType();
if (!compareParamTypes(manager, type1, type2, new HashSet<>())) return false;
}
return true;
}
private static boolean compareParamTypes(@NotNull PsiManager manager, @NotNull PsiType type1, @NotNull PsiType type2, Set<? super String> visited) {
if (type1 instanceof PsiArrayType) {
if (type2 instanceof PsiArrayType) {
PsiType componentType1 = ((PsiArrayType)type1).getComponentType();
PsiType componentType2 = ((PsiArrayType)type2).getComponentType();
if (compareParamTypes(manager, componentType1, componentType2, visited)) return true;
}
return false;
}
if (!(type1 instanceof PsiClassType) || !(type2 instanceof PsiClassType)) {
return type1.equals(type2);
}
PsiClass class1 = ((PsiClassType)type1).resolve();
PsiClass class2 = ((PsiClassType)type2).resolve();
visited.add(type1.getCanonicalText());
visited.add(type2.getCanonicalText());
if (class1 instanceof PsiTypeParameter && class2 instanceof PsiTypeParameter) {
if (!(Objects.equals(class1.getName(), class2.getName()) && ((PsiTypeParameter)class1).getIndex() == ((PsiTypeParameter)class2).getIndex())) return false;
PsiClassType[] eTypes1 = class1.getExtendsListTypes();
PsiClassType[] eTypes2 = class2.getExtendsListTypes();
if (eTypes1.length != eTypes2.length) return false;
for (int i = 0; i < eTypes1.length; i++) {
PsiClassType eType1 = eTypes1[i];
PsiClassType eType2 = eTypes2[i];
if (visited.contains(eType1.getCanonicalText()) || visited.contains(eType2.getCanonicalText())) {
return false;
}
if (!compareParamTypes(manager, eType1, eType2, visited)) return false;
}
return true;
}
return manager.areElementsEquivalent(class1, class2);
}
}