public static boolean proveExtendsBoundsDistinct(
      PsiType type1, PsiType type2, PsiClass boundClass1, PsiClass boundClass2) {
    if (boundClass1.isInterface() && boundClass2.isInterface()) return false;
    if (boundClass1.isInterface()) {
      return !(boundClass2.hasModifierProperty(PsiModifier.FINAL)
          ? InheritanceUtil.isInheritorOrSelf(boundClass2, boundClass1, true)
          : true);
    }
    if (boundClass2.isInterface()) {
      return !(boundClass1.hasModifierProperty(PsiModifier.FINAL)
          ? InheritanceUtil.isInheritorOrSelf(boundClass1, boundClass2, true)
          : true);
    }

    if (boundClass1 instanceof PsiTypeParameter) {
      return try2ProveTypeParameterDistinct(type2, boundClass1);
    }

    if (boundClass2 instanceof PsiTypeParameter) {
      return try2ProveTypeParameterDistinct(type1, boundClass2);
    }

    return !InheritanceUtil.isInheritorOrSelf(boundClass1, boundClass2, true)
        && !InheritanceUtil.isInheritorOrSelf(boundClass2, boundClass1, true);
  }
  public void updateThrowsList(PsiClassType exceptionType) {
    if (!getSuperMethods().isEmpty()) {
      for (RefMethod refSuper : getSuperMethods()) {
        ((RefMethodImpl) refSuper).updateThrowsList(exceptionType);
      }
    } else if (myUnThrownExceptions != null) {
      if (exceptionType == null) {
        myUnThrownExceptions = null;
        return;
      }
      PsiClass exceptionClass = exceptionType.resolve();
      JavaPsiFacade facade = JavaPsiFacade.getInstance(myManager.getProject());
      for (int i = myUnThrownExceptions.size() - 1; i >= 0; i--) {
        String exceptionFqn = myUnThrownExceptions.get(i);
        PsiClass classType =
            facade.findClass(
                exceptionFqn, GlobalSearchScope.allScope(getRefManager().getProject()));
        if (InheritanceUtil.isInheritorOrSelf(exceptionClass, classType, true)
            || InheritanceUtil.isInheritorOrSelf(classType, exceptionClass, true)) {
          myUnThrownExceptions.remove(i);
        }
      }

      if (myUnThrownExceptions.isEmpty()) myUnThrownExceptions = null;
    }
  }
  protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) {
    final MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();

    checkExistingMethods(conflicts, true);
    checkExistingMethods(conflicts, false);
    final Collection<PsiClass> classes = ClassInheritorsSearch.search(myClass).findAll();
    for (FieldDescriptor fieldDescriptor : myFieldDescriptors) {
      final Set<PsiMethod> setters = new HashSet<PsiMethod>();
      final Set<PsiMethod> getters = new HashSet<PsiMethod>();

      for (PsiClass aClass : classes) {
        final PsiMethod getterOverrider =
            myDescriptor.isToEncapsulateGet()
                ? aClass.findMethodBySignature(fieldDescriptor.getGetterPrototype(), false)
                : null;
        if (getterOverrider != null) {
          getters.add(getterOverrider);
        }
        final PsiMethod setterOverrider =
            myDescriptor.isToEncapsulateSet()
                ? aClass.findMethodBySignature(fieldDescriptor.getSetterPrototype(), false)
                : null;
        if (setterOverrider != null) {
          setters.add(setterOverrider);
        }
      }
      if (!getters.isEmpty() || !setters.isEmpty()) {
        final PsiField field = fieldDescriptor.getField();
        for (PsiReference reference : ReferencesSearch.search(field)) {
          final PsiElement place = reference.getElement();
          if (place instanceof PsiReferenceExpression) {
            final PsiExpression qualifierExpression =
                ((PsiReferenceExpression) place).getQualifierExpression();
            final PsiClass ancestor;
            if (qualifierExpression == null) {
              ancestor = PsiTreeUtil.getParentOfType(place, PsiClass.class, false);
            } else {
              ancestor = PsiUtil.resolveClassInType(qualifierExpression.getType());
            }

            final boolean isGetter = !PsiUtil.isAccessedForWriting((PsiExpression) place);
            for (PsiMethod overridden : isGetter ? getters : setters) {
              if (InheritanceUtil.isInheritorOrSelf(myClass, ancestor, true)) {
                conflicts.putValue(
                    overridden,
                    "There is already a "
                        + RefactoringUIUtil.getDescription(overridden, true)
                        + " which would hide generated "
                        + (isGetter ? "getter" : "setter")
                        + " for "
                        + place.getText());
                break;
              }
            }
          }
        }
      }
    }
    return showConflicts(conflicts, refUsages.get());
  }
 private static PsiSubstitutor calculateSubstitutor(
     PsiMethod derivedMethod, PsiMethod baseMethod) {
   PsiSubstitutor substitutor;
   if (derivedMethod.getManager().areElementsEquivalent(derivedMethod, baseMethod)) {
     substitutor = PsiSubstitutor.EMPTY;
   } else {
     final PsiClass baseClass = baseMethod.getContainingClass();
     final PsiClass derivedClass = derivedMethod.getContainingClass();
     if (baseClass != null
         && derivedClass != null
         && InheritanceUtil.isInheritorOrSelf(derivedClass, baseClass, true)) {
       final PsiSubstitutor superClassSubstitutor =
           TypeConversionUtil.getSuperClassSubstitutor(
               baseClass, derivedClass, PsiSubstitutor.EMPTY);
       final MethodSignature superMethodSignature = baseMethod.getSignature(superClassSubstitutor);
       final MethodSignature methodSignature = derivedMethod.getSignature(PsiSubstitutor.EMPTY);
       final PsiSubstitutor superMethodSubstitutor =
           MethodSignatureUtil.getSuperMethodSignatureSubstitutor(
               methodSignature, superMethodSignature);
       substitutor =
           superMethodSubstitutor != null ? superMethodSubstitutor : superClassSubstitutor;
     } else {
       substitutor = PsiSubstitutor.EMPTY;
     }
   }
   return substitutor;
 }
  private static boolean isCollectCall(PsiStatement body, final PsiParameter parameter) {
    PsiIfStatement ifStatement = extractIfStatement(body);
    final PsiMethodCallExpression methodCallExpression = extractAddCall(body, ifStatement);
    if (methodCallExpression != null) {
      final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
      final PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
      PsiClass qualifierClass = null;
      if (qualifierExpression instanceof PsiReferenceExpression) {
        if (ReferencesSearch.search(parameter, new LocalSearchScope(qualifierExpression))
                .findFirst()
            != null) {
          return false;
        }
        final PsiElement resolve = ((PsiReferenceExpression) qualifierExpression).resolve();
        if (resolve instanceof PsiVariable) {
          if (ReferencesSearch.search(
                      resolve, new LocalSearchScope(methodCallExpression.getArgumentList()))
                  .findFirst()
              != null) {
            return false;
          }
        }
        qualifierClass = PsiUtil.resolveClassInType(qualifierExpression.getType());
      } else if (qualifierExpression == null) {
        final PsiClass enclosingClass = PsiTreeUtil.getParentOfType(body, PsiClass.class);
        if (PsiUtil.getEnclosingStaticElement(body, enclosingClass) == null) {
          qualifierClass = enclosingClass;
        }
      }

      if (qualifierClass != null
          && InheritanceUtil.isInheritor(
              qualifierClass, false, CommonClassNames.JAVA_UTIL_COLLECTION)) {

        while (ifStatement != null && PsiTreeUtil.isAncestor(body, ifStatement, false)) {
          final PsiExpression condition = ifStatement.getCondition();
          if (condition != null
              && isConditionDependsOnUpdatedCollections(condition, qualifierExpression))
            return false;
          ifStatement = PsiTreeUtil.getParentOfType(ifStatement, PsiIfStatement.class);
        }

        final PsiElement resolve = methodExpression.resolve();
        if (resolve instanceof PsiMethod
            && "add".equals(((PsiMethod) resolve).getName())
            && ((PsiMethod) resolve).getParameterList().getParametersCount() == 1) {
          final PsiExpression[] args = methodCallExpression.getArgumentList().getExpressions();
          if (args.length == 1) {
            if (args[0] instanceof PsiCallExpression) {
              final PsiMethod method = ((PsiCallExpression) args[0]).resolveMethod();
              return method != null && !method.hasTypeParameters() && !isThrowsCompatible(method);
            }
            return true;
          }
        }
      }
    }
    return false;
  }
  private static boolean isSuperMethod(
      PsiClass aClass,
      HierarchicalMethodSignature hierarchicalMethodSignature,
      HierarchicalMethodSignature superSignatureHierarchical) {
    PsiMethod superMethod = superSignatureHierarchical.getMethod();
    PsiClass superClass = superMethod.getContainingClass();
    PsiClass containingClass = hierarchicalMethodSignature.getMethod().getContainingClass();
    if (!superMethod.isConstructor()) {
      if (!aClass.equals(superClass)) {
        if (PsiUtil.isAccessible(aClass.getProject(), superMethod, aClass, aClass)) {
          if (MethodSignatureUtil.isSubsignature(
              superSignatureHierarchical, hierarchicalMethodSignature)) {
            if (superClass != null) {
              if (superClass.isInterface()
                  || CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName())) {
                if (superMethod.hasModifierProperty(PsiModifier.DEFAULT)
                    || hierarchicalMethodSignature
                        .getMethod()
                        .hasModifierProperty(PsiModifier.DEFAULT)) {
                  return !InheritanceUtil.isInheritorOrSelf(superClass, containingClass, true);
                }
                return true;
              }

              if (containingClass != null) {
                if (containingClass.isInterface()) {
                  return false;
                }

                if (!aClass.isInterface()
                    && !InheritanceUtil.isInheritorOrSelf(superClass, containingClass, true)) {
                  return true;
                }
              }
            }
          }
        }
      }
    }
    return false;
  }
  public static boolean provablyDistinct(PsiWildcardType type1, PsiWildcardType type2) {
    if (type1.isSuper() && type2.isSuper()) return false;
    if (type1.isExtends() && type2.isExtends()) {
      final PsiType extendsBound1 = type1.getExtendsBound();
      final PsiType extendsBound2 = type2.getExtendsBound();
      if (extendsBound1 instanceof PsiArrayType
              && proveArrayTypeDistinct(
                  type1.getManager().getProject(), (PsiArrayType) extendsBound1, extendsBound2)
          || extendsBound2 instanceof PsiArrayType
              && proveArrayTypeDistinct(
                  type1.getManager().getProject(), (PsiArrayType) extendsBound2, extendsBound1))
        return true;

      final PsiClass boundClass1 = PsiUtil.resolveClassInType(extendsBound1);
      final PsiClass boundClass2 = PsiUtil.resolveClassInType(extendsBound2);
      if (boundClass1 != null && boundClass2 != null) {
        return proveExtendsBoundsDistinct(type1, type2, boundClass1, boundClass2);
      }
      return provablyDistinct(extendsBound1, extendsBound2, 1);
    }
    if (type2.isExtends()) return provablyDistinct(type2, type1);
    if (type1.isExtends() && type2.isSuper()) {
      final PsiType extendsBound = type1.getExtendsBound();
      final PsiType superBound = type2.getSuperBound();
      if (extendsBound instanceof PsiArrayType
              && proveArrayTypeDistinct(
                  type1.getManager().getProject(), (PsiArrayType) extendsBound, superBound)
          || superBound instanceof PsiArrayType
              && proveArrayTypeDistinct(
                  type1.getManager().getProject(), (PsiArrayType) superBound, extendsBound))
        return true;

      final PsiClass extendsBoundClass = PsiUtil.resolveClassInType(extendsBound);
      final PsiClass superBoundClass = PsiUtil.resolveClassInType(superBound);
      if (extendsBoundClass != null && superBoundClass != null) {
        if (extendsBoundClass instanceof PsiTypeParameter) {
          return try2ProveTypeParameterDistinct(type2, extendsBoundClass);
        }
        if (superBoundClass instanceof PsiTypeParameter) return false;
        return !InheritanceUtil.isInheritorOrSelf(superBoundClass, extendsBoundClass, true);
      }
      return true;
    }

    if (!type1.isBounded() || !type2.isBounded()) {
      return false;
    }
    return !type1.equals(type2);
  }
  @Nullable
  public static String getSuspiciousMethodCallMessage(
      @NotNull PsiMethodCallExpression methodCall,
      PsiType argType,
      boolean reportConvertibleMethodCalls,
      @NotNull List<PsiMethod> patternMethods,
      @NotNull IntArrayList indices) {
    final PsiReferenceExpression methodExpression = methodCall.getMethodExpression();
    final PsiExpression qualifier = methodExpression.getQualifierExpression();
    if (qualifier == null
        || qualifier instanceof PsiThisExpression
        || qualifier instanceof PsiSuperExpression) return null;
    if (argType instanceof PsiPrimitiveType) {
      argType = ((PsiPrimitiveType) argType).getBoxedType(methodCall);
    }

    if (!(argType instanceof PsiClassType)) return null;

    final JavaResolveResult resolveResult = methodExpression.advancedResolve(false);
    PsiMethod calleeMethod = (PsiMethod) resolveResult.getElement();
    if (calleeMethod == null) return null;
    PsiMethod contextMethod = PsiTreeUtil.getParentOfType(methodCall, PsiMethod.class);

    //noinspection SynchronizationOnLocalVariableOrMethodParameter
    synchronized (patternMethods) {
      if (patternMethods.isEmpty()) {
        setupPatternMethods(
            methodCall.getManager(), methodCall.getResolveScope(), patternMethods, indices);
      }
    }

    for (int i = 0; i < patternMethods.size(); i++) {
      PsiMethod patternMethod = patternMethods.get(i);
      if (!patternMethod.getName().equals(methodExpression.getReferenceName())) continue;
      int index = indices.get(i);

      // we are in collections method implementation
      if (contextMethod != null && isInheritorOrSelf(contextMethod, patternMethod)) return null;

      final PsiClass calleeClass = calleeMethod.getContainingClass();
      PsiSubstitutor substitutor = resolveResult.getSubstitutor();
      final PsiClass patternClass = patternMethod.getContainingClass();
      assert patternClass != null;
      assert calleeClass != null;
      substitutor = TypeConversionUtil.getClassSubstitutor(patternClass, calleeClass, substitutor);
      if (substitutor == null) continue;

      if (!patternMethod
          .getSignature(substitutor)
          .equals(calleeMethod.getSignature(PsiSubstitutor.EMPTY))) continue;

      PsiTypeParameter[] typeParameters = patternClass.getTypeParameters();
      if (typeParameters.length <= index) return null;
      final PsiTypeParameter typeParameter = typeParameters[index];
      PsiType typeParamMapping = substitutor.substitute(typeParameter);
      if (typeParamMapping == null) return null;

      PsiParameter[] parameters = patternMethod.getParameterList().getParameters();
      if (parameters.length == 1 && "removeAll".equals(patternMethod.getName())) {
        PsiType paramType = parameters[0].getType();
        if (InheritanceUtil.isInheritor(paramType, CommonClassNames.JAVA_UTIL_COLLECTION)) {
          PsiType qualifierType = qualifier.getType();
          if (qualifierType != null) {
            final PsiType itemType =
                JavaGenericsUtil.getCollectionItemType(argType, calleeMethod.getResolveScope());
            final PsiType qualifierItemType =
                JavaGenericsUtil.getCollectionItemType(
                    qualifierType, calleeMethod.getResolveScope());
            if (qualifierItemType != null
                && itemType != null
                && !qualifierItemType.isAssignableFrom(itemType)) {
              return InspectionsBundle.message(
                  "inspection.suspicious.collections.method.calls.problem.descriptor",
                  PsiFormatUtil.formatType(qualifierType, 0, PsiSubstitutor.EMPTY),
                  PsiFormatUtil.formatType(itemType, 0, PsiSubstitutor.EMPTY));
            }
          }
          return null;
        }
      }

      String message = null;
      if (typeParamMapping instanceof PsiCapturedWildcardType) {
        typeParamMapping = ((PsiCapturedWildcardType) typeParamMapping).getWildcard();
      }
      if (!typeParamMapping.isAssignableFrom(argType)) {
        if (typeParamMapping.isConvertibleFrom(argType)) {
          if (reportConvertibleMethodCalls) {
            message =
                InspectionsBundle.message(
                    "inspection.suspicious.collections.method.calls.problem.descriptor1",
                    PsiFormatUtil.formatMethod(
                        calleeMethod,
                        substitutor,
                        PsiFormatUtilBase.SHOW_NAME | PsiFormatUtilBase.SHOW_CONTAINING_CLASS,
                        PsiFormatUtilBase.SHOW_TYPE));
          }
        } else {
          PsiType qualifierType = qualifier.getType();
          if (qualifierType != null) {
            message =
                InspectionsBundle.message(
                    "inspection.suspicious.collections.method.calls.problem.descriptor",
                    PsiFormatUtil.formatType(qualifierType, 0, PsiSubstitutor.EMPTY),
                    PsiFormatUtil.formatType(argType, 0, PsiSubstitutor.EMPTY));
          }
        }
      }
      return message;
    }
    return null;
  }
  private void checkExistingMethods(MultiMap<PsiElement, String> conflicts, boolean isGetter) {
    if (isGetter) {
      if (!myDescriptor.isToEncapsulateGet()) return;
    } else {
      if (!myDescriptor.isToEncapsulateSet()) return;
    }

    for (FieldDescriptor descriptor : myFieldDescriptors) {
      PsiMethod prototype =
          isGetter ? descriptor.getGetterPrototype() : descriptor.getSetterPrototype();

      final PsiType prototypeReturnType = prototype.getReturnType();
      PsiMethod existing = myClass.findMethodBySignature(prototype, true);
      if (existing != null) {
        final PsiType returnType = existing.getReturnType();
        if (!RefactoringUtil.equivalentTypes(
            prototypeReturnType, returnType, myClass.getManager())) {
          final String descr =
              PsiFormatUtil.formatMethod(
                  existing,
                  PsiSubstitutor.EMPTY,
                  PsiFormatUtilBase.SHOW_NAME
                      | PsiFormatUtilBase.SHOW_PARAMETERS
                      | PsiFormatUtilBase.SHOW_TYPE,
                  PsiFormatUtilBase.SHOW_TYPE);
          String message =
              isGetter
                  ? RefactoringBundle.message(
                      "encapsulate.fields.getter.exists",
                      CommonRefactoringUtil.htmlEmphasize(descr),
                      CommonRefactoringUtil.htmlEmphasize(prototype.getName()))
                  : RefactoringBundle.message(
                      "encapsulate.fields.setter.exists",
                      CommonRefactoringUtil.htmlEmphasize(descr),
                      CommonRefactoringUtil.htmlEmphasize(prototype.getName()));
          conflicts.putValue(existing, message);
        }
      } else {
        PsiClass containingClass = myClass.getContainingClass();
        while (containingClass != null && existing == null) {
          existing = containingClass.findMethodBySignature(prototype, true);
          if (existing != null) {
            for (PsiReference reference : ReferencesSearch.search(existing)) {
              final PsiElement place = reference.getElement();
              LOG.assertTrue(place instanceof PsiReferenceExpression);
              final PsiExpression qualifierExpression =
                  ((PsiReferenceExpression) place).getQualifierExpression();
              final PsiClass inheritor;
              if (qualifierExpression == null) {
                inheritor = PsiTreeUtil.getParentOfType(place, PsiClass.class, false);
              } else {
                inheritor = PsiUtil.resolveClassInType(qualifierExpression.getType());
              }

              if (InheritanceUtil.isInheritorOrSelf(inheritor, myClass, true)) {
                conflicts.putValue(
                    existing,
                    "There is already a "
                        + RefactoringUIUtil.getDescription(existing, true)
                        + " which would be hidden by generated "
                        + (isGetter ? "getter" : "setter"));
                break;
              }
            }
          }
          containingClass = containingClass.getContainingClass();
        }
      }
    }
  }
Example #10
0
  private String[] suggestVariableNameByType(
      final PsiType type,
      final VariableKind variableKind,
      final boolean correctKeywords,
      boolean skipIndices) {
    String longTypeName = skipIndices ? type.getCanonicalText() : getLongTypeName(type);
    CodeStyleSettings.TypeToNameMap map = getMapByVariableKind(variableKind);
    if (map != null && longTypeName != null) {
      if (type.equals(PsiType.NULL)) {
        longTypeName = CommonClassNames.JAVA_LANG_OBJECT;
      }
      String name = map.nameByType(longTypeName);
      if (name != null && isIdentifier(name)) {
        return new String[] {name};
      }
    }

    final Collection<String> suggestions = new LinkedHashSet<String>();

    final PsiClass psiClass =
        !skipIndices && type instanceof PsiClassType ? ((PsiClassType) type).resolve() : null;

    if (!skipIndices) {
      suggestNamesForCollectionInheritors(type, variableKind, suggestions, correctKeywords);

      if (psiClass != null
          && CommonClassNames.JAVA_UTIL_OPTIONAL.equals(psiClass.getQualifiedName())
          && ((PsiClassType) type).getParameterCount() == 1) {
        PsiType optionalContent = ((PsiClassType) type).getParameters()[0];
        Collections.addAll(
            suggestions,
            suggestVariableNameByType(optionalContent, variableKind, correctKeywords, false));
      }

      suggestNamesFromGenericParameters(type, variableKind, suggestions, correctKeywords);
    }

    String typeName = getTypeName(type, !skipIndices);

    if (typeName != null) {
      typeName = normalizeTypeName(typeName);
      ContainerUtil.addAll(
          suggestions,
          getSuggestionsByName(
              typeName, variableKind, type instanceof PsiArrayType, correctKeywords));
    }

    if (psiClass != null && psiClass.getContainingClass() != null) {
      InheritanceUtil.processSupers(
          psiClass,
          false,
          new Processor<PsiClass>() {
            @Override
            public boolean process(PsiClass superClass) {
              if (PsiTreeUtil.isAncestor(superClass, psiClass, true)) {
                ContainerUtil.addAll(
                    suggestions,
                    getSuggestionsByName(
                        superClass.getName(), variableKind, false, correctKeywords));
              }
              return false;
            }
          });
    }

    return ArrayUtil.toStringArray(suggestions);
  }
  private static boolean provablyDistinct(PsiType type1, PsiType type2, int level) {
    if (type1 instanceof PsiClassType
        && ((PsiClassType) type1).resolve() instanceof PsiTypeParameter) return false;
    if (type2 instanceof PsiClassType
        && ((PsiClassType) type2).resolve() instanceof PsiTypeParameter) return false;
    if (type1 instanceof PsiWildcardType) {
      if (type2 instanceof PsiWildcardType) {
        return provablyDistinct((PsiWildcardType) type1, (PsiWildcardType) type2);
      }

      if (type2 instanceof PsiCapturedWildcardType) {
        return ((PsiWildcardType) type1).isExtends() && level > 0
            || provablyDistinct(
                (PsiWildcardType) type1, ((PsiCapturedWildcardType) type2).getWildcard());
      }

      if (type2 instanceof PsiClassType) {
        final PsiClass psiClass2 = PsiUtil.resolveClassInType(type2);
        if (psiClass2 == null) return false;

        if (((PsiWildcardType) type1).isExtends()) {
          final PsiType extendsBound = ((PsiWildcardType) type1).getExtendsBound();
          if (extendsBound instanceof PsiArrayType
              && proveArrayTypeDistinct(
                  ((PsiWildcardType) type1).getManager().getProject(),
                  (PsiArrayType) extendsBound,
                  type2)) return true;
          final PsiClass boundClass1 = PsiUtil.resolveClassInType(extendsBound);
          if (boundClass1 == null) return false;
          return proveExtendsBoundsDistinct(type1, type2, boundClass1, psiClass2);
        }

        if (((PsiWildcardType) type1).isSuper()) {
          final PsiType superBound = ((PsiWildcardType) type1).getSuperBound();
          if (superBound instanceof PsiArrayType
              && proveArrayTypeDistinct(
                  ((PsiWildcardType) type1).getManager().getProject(),
                  (PsiArrayType) superBound,
                  type2)) return true;

          final PsiClass boundClass1 = PsiUtil.resolveClassInType(superBound);
          if (boundClass1 == null || boundClass1 instanceof PsiTypeParameter) return false;
          return !InheritanceUtil.isInheritorOrSelf(boundClass1, psiClass2, true);
        }

        final PsiType bound = ((PsiWildcardType) type1).getBound();
        return bound != null && !bound.equals(psiClass2);
      }

      if (type2 instanceof PsiArrayType) {
        return proveArrayTypeDistinct(
            ((PsiWildcardType) type1).getManager().getProject(), (PsiArrayType) type2, type1);
      }
    }
    if (type1 instanceof PsiCapturedWildcardType)
      return provablyDistinct(((PsiCapturedWildcardType) type1).getWildcard(), type2, level);

    if (type2 instanceof PsiWildcardType || type2 instanceof PsiCapturedWildcardType)
      return provablyDistinct(type2, type1, level);

    final PsiClassType.ClassResolveResult classResolveResult1 =
        PsiUtil.resolveGenericsClassInType(type1);
    final PsiClassType.ClassResolveResult classResolveResult2 =
        PsiUtil.resolveGenericsClassInType(type2);
    if (Comparing.equal(TypeConversionUtil.erasure(type1), TypeConversionUtil.erasure(type2))) {
      final PsiSubstitutor substitutor1 = classResolveResult1.getSubstitutor();
      final PsiSubstitutor substitutor2 = classResolveResult2.getSubstitutor();
      for (PsiTypeParameter parameter : substitutor1.getSubstitutionMap().keySet()) {
        final PsiType substitutedType1 = substitutor1.substitute(parameter);
        final PsiType substitutedType2 = substitutor2.substitute(parameter);
        if (substitutedType1 == null && substitutedType2 == null) return false;
        if (substitutedType1 == null || substitutedType2 == null) {
          return true;
        } else {
          if (provablyDistinct(substitutedType1, substitutedType2, level + 1)) return true;
          if (substitutedType1 instanceof PsiWildcardType
              && !((PsiWildcardType) substitutedType1).isBounded()) return true;
        }
      }
      return false;
    }

    final PsiClass boundClass1 = classResolveResult1.getElement();
    final PsiClass boundClass2 = classResolveResult2.getElement();
    return type2 != null
        && type1 != null
        && !type1.equals(type2)
        && (!InheritanceUtil.isInheritorOrSelf(boundClass1, boundClass2, true)
            || !InheritanceUtil.isInheritorOrSelf(boundClass2, boundClass1, true));
  }
  protected void checkSameSignatures(@NotNull List<CandidateInfo> conflicts) {
    // candidates should go in order of class hierarchy traversal
    // in order for this to work
    Map<MethodSignature, CandidateInfo> signatures =
        new THashMap<MethodSignature, CandidateInfo>(conflicts.size());
    Set<PsiMethod> superMethods = new HashSet<PsiMethod>();
    for (CandidateInfo conflict : conflicts) {
      final PsiMethod method = ((MethodCandidateInfo) conflict).getElement();
      for (HierarchicalMethodSignature methodSignature :
          method.getHierarchicalMethodSignature().getSuperSignatures()) {
        final PsiMethod superMethod = methodSignature.getMethod();
        final PsiClass aClass = superMethod.getContainingClass();
        if (aClass != null
            && !CommonClassNames.JAVA_LANG_OBJECT.equals(aClass.getQualifiedName())) {
          superMethods.add(superMethod);
        }
      }
    }
    nextConflict:
    for (int i = 0; i < conflicts.size(); i++) {
      ProgressManager.checkCanceled();
      CandidateInfo info = conflicts.get(i);
      PsiMethod method = (PsiMethod) info.getElement();

      if (!method.hasModifierProperty(PsiModifier.STATIC) && superMethods.contains(method)) {
        conflicts.remove(i);
        i--;
        continue;
      }

      PsiClass class1 = method.getContainingClass();
      PsiSubstitutor infoSubstitutor = ((MethodCandidateInfo) info).getSubstitutor(false);
      MethodSignature signature = method.getSignature(infoSubstitutor);
      CandidateInfo existing = signatures.get(signature);

      if (existing == null) {
        signatures.put(signature, info);
        continue;
      }
      PsiMethod existingMethod = (PsiMethod) existing.getElement();
      PsiClass existingClass = existingMethod.getContainingClass();
      if (class1 != null
          && existingClass != null) { // prefer interface methods to methods from Object
        if (class1.isInterface()
            && CommonClassNames.JAVA_LANG_OBJECT.equals(existingClass.getQualifiedName())) {
          signatures.put(signature, info);
          continue;
        } else if (existingClass.isInterface()
            && CommonClassNames.JAVA_LANG_OBJECT.equals(class1.getQualifiedName())) {
          conflicts.remove(info);
          i--;
          continue;
        }
      }
      if (method == existingMethod) {
        PsiElement scope1 = info.getCurrentFileResolveScope();
        PsiElement scope2 = existing.getCurrentFileResolveScope();
        if (scope1 instanceof PsiClass
            && scope2 instanceof PsiClass
            && PsiTreeUtil.isAncestor(scope1, scope2, true)
            && !existing
                .isAccessible()) { // prefer methods from outer class to inaccessible base class
                                   // methods
          signatures.put(signature, info);
          continue;
        }
      }

      // filter out methods with incorrect inferred bounds (for unrelated methods only)
      boolean existingTypeParamAgree = areTypeParametersAgree(existing);
      boolean infoTypeParamAgree = areTypeParametersAgree(info);
      if (existingTypeParamAgree
          && !infoTypeParamAgree
          && !PsiSuperMethodImplUtil.isSuperMethodSmart(method, existingMethod)) {
        conflicts.remove(i);
        i--;
        continue;
      }
      if (!existingTypeParamAgree
          && infoTypeParamAgree
          && !PsiSuperMethodImplUtil.isSuperMethodSmart(existingMethod, method)) {
        signatures.put(signature, info);
        int index = conflicts.indexOf(existing);
        conflicts.remove(index);
        i--;
        continue;
      }

      if (InheritanceUtil.isInheritorOrSelf(class1, existingClass, true)
          || InheritanceUtil.isInheritorOrSelf(existingClass, class1, true)) {
        PsiParameter[] parameters = method.getParameterList().getParameters();
        final PsiParameter[] existingParameters = existingMethod.getParameterList().getParameters();
        for (int i1 = 0, parametersLength = parameters.length; i1 < parametersLength; i1++) {
          if (parameters[i1].getType() instanceof PsiArrayType
              && !(existingParameters[i1].getType()
                  instanceof PsiArrayType)) { // prefer more specific type
            signatures.put(signature, info);
            continue nextConflict;
          }
        }
        PsiType returnType1 = method.getReturnType();
        PsiType returnType2 = existingMethod.getReturnType();
        if (returnType1 != null && returnType2 != null) {
          returnType1 = infoSubstitutor.substitute(returnType1);
          returnType2 =
              ((MethodCandidateInfo) existing).getSubstitutor(false).substitute(returnType2);
          if (!returnType1.equals(returnType2) && returnType1.isAssignableFrom(returnType2)) {
            conflicts.remove(i);
            i--;
            continue;
          }
        }

        // prefer derived class
        signatures.put(signature, info);
      } else {
        final PsiMethodCallExpression methodCallExpression =
            PsiTreeUtil.getParentOfType(myArgumentsList, PsiMethodCallExpression.class);
        if (methodCallExpression != null) {
          final PsiReferenceExpression expression = methodCallExpression.getMethodExpression();
          final PsiExpression qualifierExpression = expression.getQualifierExpression();
          PsiClass currentClass;
          if (qualifierExpression != null) {
            currentClass = PsiUtil.resolveClassInClassTypeOnly(qualifierExpression.getType());
          } else {
            currentClass = PsiTreeUtil.getParentOfType(expression, PsiClass.class);
          }

          if (currentClass != null && existingClass != null && class1 != null) {
            final PsiSubstitutor eSubstitutor =
                TypeConversionUtil.getMaybeSuperClassSubstitutor(
                    existingClass, currentClass, PsiSubstitutor.EMPTY, null);
            final PsiSubstitutor cSubstitutor =
                TypeConversionUtil.getMaybeSuperClassSubstitutor(
                    class1, currentClass, PsiSubstitutor.EMPTY, null);
            if (eSubstitutor != null
                && cSubstitutor != null
                && MethodSignatureUtil.areSignaturesEqual(
                    existingMethod.getSignature(eSubstitutor), method.getSignature(cSubstitutor))) {
              final PsiType returnType = eSubstitutor.substitute(existingMethod.getReturnType());
              final PsiType returnType1 = cSubstitutor.substitute(method.getReturnType());
              if (returnType != null && returnType1 != null && !returnType1.equals(returnType)) {
                if (TypeConversionUtil.isAssignable(returnType, returnType1, false)) {
                  if (class1.isInterface() && !existingClass.isInterface()) continue;
                  conflicts.remove(existing);
                } else {
                  if (!TypeConversionUtil.isAssignable(returnType1, returnType, false)) continue;
                  conflicts.remove(i);
                }
                i--;
                break;
              }
            }
          }
        }
      }
    }
  }