boolean (@NotNull PsiSubstitutor s1, @NotNull PsiClass aClass, @NotNull PsiSubstitutor s2, @NotNull PsiClass bClass) { if (s1 == s2 && aClass == bClass) return true; // assume generic class equals to non-generic if (aClass.hasTypeParameters() != bClass.hasTypeParameters()) return true; PsiTypeParameter[] typeParameters1 = aClass.getTypeParameters(); PsiTypeParameter[] typeParameters2 = bClass.getTypeParameters(); if (typeParameters1.length != typeParameters2.length) return false; for (int i = 0; i < typeParameters1.length; i++) { PsiType substituted2 = s2.substitute(typeParameters2[i]); PsiType substituted1 = s1.substitute(typeParameters1[i]); if (!Comparing.equal(substituted1, substituted2)) return false; } if (aClass.hasModifierProperty(PsiModifier.STATIC)) return true; PsiClass containingClass1 = aClass.getContainingClass(); PsiClass containingClass2 = bClass.getContainingClass(); if (containingClass1 != null && containingClass2 != null) { return equalOnEquivalentClasses(s1, containingClass1, s2, containingClass2); } if (containingClass1 == null && containingClass2 == null) { if (aClass == bClass && isLocalClass(aClass)) { PsiClass containingClass = PsiTreeUtil.getParentOfType(aClass, PsiClass.class); return containingClass != null && equalOnEquivalentClasses(s1, containingClass, s2, containingClass); } return true; } return false; }