boolean addMigrationRoot(PsiElement element,
                           PsiType type,
                           PsiElement place,
                           boolean alreadyProcessed,
                           boolean isContraVariantPosition,
                           boolean userDefinedType) {
    if (myAllowedRoots != null && !myAllowedRoots.contains(element)) {
      return false;
    }
    if (type.equals(PsiTypes.nullType()) ||
        PsiTypes.voidType().equals(type) && myCurrentRoot != null) {
      return false;
    }
    final PsiElement resolved = Util.normalizeElement(element);
    if (!canBeRoot(resolved, myRules.getSearchScope()) || resolved instanceof PsiEnumConstant) {
      return false;
    }
    final PsiType originalType = getElementType(resolved);
    LOG.assertTrue(originalType != null);
    type = userDefinedType ? type : TypeEvaluator.substituteType(type, originalType, isContraVariantPosition);

    if (userDefinedType) {
      Set<PsiTypeParameter> disappearedTypeParameters = getTypeParameters(originalType);
      disappearedTypeParameters.removeAll(getTypeParameters(type));
      myDisappearedTypeParameters.addAll(disappearedTypeParameters);
    }
    else if (typeContainsTypeParameters(originalType, getTypeParameters(type))) return false;

    if (type instanceof PsiCapturedWildcardType) return false;

    if (resolved instanceof PsiMethod method) {
      if (method.getContainingClass() instanceof PsiAnonymousClass anonymousClass) {
        final HierarchicalMethodSignature signature = method.getHierarchicalMethodSignature();
        final List<HierarchicalMethodSignature> superSignatures = signature.getSuperSignatures();
        if (!superSignatures.isEmpty()) {
          final HierarchicalMethodSignature superSignature = superSignatures.get(0);

          final PsiSubstitutor substitutor = superSignature.getSubstitutor();
          if (!substitutor.getSubstitutionMap().isEmpty()) {
            final PsiMethod superMethod = superSignature.getMethod();

            if (superMethod.getReturnType() instanceof PsiClassType superReturnType &&
                superReturnType.resolve() instanceof PsiTypeParameter returnTypeParameter) {
              final PsiType expectedReturnType = substitutor.substitute(returnTypeParameter);
              if (Comparing.equal(expectedReturnType, method.getReturnType())) {
                final PsiClassType baseClassType = anonymousClass.getBaseClassType();
                final PsiClassType.ClassResolveResult result = baseClassType.resolveGenerics();
                final PsiClass anonymousBaseClass = result.getElement();

                if (anonymousBaseClass != null) {
                  final PsiSubstitutor superHierarchySubstitutor = TypeConversionUtil
                    .getClassSubstitutor(superMethod.getContainingClass(), anonymousBaseClass, PsiSubstitutor.EMPTY);
                  final PsiType maybeTypeParameter = superHierarchySubstitutor.substitute(returnTypeParameter);

                  if (maybeTypeParameter instanceof PsiClassType classType &&
                      classType.resolve() instanceof PsiTypeParameter typeParameter) {
                    final PsiSubstitutor newSubstitutor = result.getSubstitutor().put(typeParameter, type);
                    addRoot(new TypeMigrationUsageInfo(anonymousClass.getBaseClassReference().getParameterList()),
                            new PsiImmediateClassType(anonymousBaseClass, newSubstitutor),
                            place,
                            alreadyProcessed);
                  }
                }
              }
            }
          }
        }
      }

      final PsiMethod[] methods = OverridingMethodsSearch.search(method).toArray(PsiMethod.EMPTY_ARRAY);
      final OverriderUsageInfo[] overriders = new OverriderUsageInfo[methods.length];
      for (int i = -1; i < methods.length; i++) {
        final TypeMigrationUsageInfo m;
        if (i < 0) {
          final OverriddenUsageInfo overriddenUsageInfo = new OverriddenUsageInfo(method);
          m = overriddenUsageInfo;
          final String newMethodName = isMethodNameCanBeChanged(method);
          if (newMethodName != null) {
            final MigrateGetterNameSetting migrateGetterNameSetting = myRules.getConversionSettings(MigrateGetterNameSetting.class);
            migrateGetterNameSetting.askUserIfNeeded(overriddenUsageInfo, newMethodName, myTypeEvaluator.getType(myCurrentRoot));
          }
        }
        else {
          overriders[i] = new OverriderUsageInfo(methods[i], method);
          m = overriders[i];
        }

        alreadyProcessed = addRoot(m, type, place, alreadyProcessed);
      }

      return !alreadyProcessed;
    }
    else if (resolved instanceof PsiParameter parameter && parameter.getDeclarationScope() instanceof PsiMethod method) {
      final int index = method.getParameterList().getParameterIndex(parameter);
      final PsiMethod[] methods = OverridingMethodsSearch.search(method).toArray(PsiMethod.EMPTY_ARRAY);

      final OverriderUsageInfo[] overriders = new OverriderUsageInfo[methods.length];
      final OverriddenUsageInfo overriddenUsageInfo = new OverriddenUsageInfo(method.getParameterList().getParameters()[index]);
      for (int i = -1; i < methods.length; i++) {
        final PsiMethod m = i < 0 ? method : methods[i];
        final PsiParameter p = m.getParameterList().getParameters()[index];
        final TypeMigrationUsageInfo paramUsageInfo;
        if (i < 0) {
          paramUsageInfo = overriddenUsageInfo;
        }
        else {
          overriders[i] = new OverriderUsageInfo(p, method);
          paramUsageInfo = overriders[i];
        }
        alreadyProcessed = addRoot(paramUsageInfo, type, place, alreadyProcessed);
      }

      return !alreadyProcessed;
    }
    else {
      return !addRoot(new TypeMigrationUsageInfo(resolved), type, place, alreadyProcessed);
    }
  }
boolean addMigrationRoot(PsiElement element,
                           PsiType type,
                           PsiElement place,
                           boolean alreadyProcessed,
                           boolean isContraVariantPosition,
                           boolean userDefinedType)