private @Nullable PyType replaceSelf(@Nullable PyType returnType,
                                       @Nullable PyExpression receiver,
                                       @NotNull TypeEvalContext context,
                                       boolean allowCoroutineOrGenerator) {
    if (receiver != null) {
      // TODO: Currently we substitute only simple subclass types and unions, but we could handle collection types as well
      if (returnType instanceof PyClassType returnClassType) {

        if (returnClassType.getPyClass() == getContainingClass()) {
          final PyType receiverType = context.getType(receiver);

          if (receiverType instanceof PyClassType receiverClassType) {

            if (receiverClassType.getPyClass() != returnClassType.getPyClass() &&
                PyTypeChecker.match(returnClassType.toClass(), receiverClassType.toClass(), context)) {
              return returnClassType.isDefinition() ? receiverClassType.toClass() : receiverClassType.toInstance();
            }
          }
        }
        else if (allowCoroutineOrGenerator &&
                 returnType instanceof PyCollectionType &&
                 PyTypingTypeProvider.coroutineOrGeneratorElementType(returnType) != null) {
          final List<PyType> replacedElementTypes = ContainerUtil.map(
            ((PyCollectionType)returnType).getElementTypes(),
            type -> replaceSelf(type, receiver, context, false)
          );

          return new PyCollectionTypeImpl(returnClassType.getPyClass(),
                                          returnClassType.isDefinition(),
                                          replacedElementTypes);
        }
      }
      else if (returnType instanceof PyUnionType) {
        return ((PyUnionType)returnType).map(type -> replaceSelf(type, receiver, context, true));
      }
    }
    return returnType;
  }
private @Nullable PyType replaceSelf(@Nullable PyType returnType,
                                       @Nullable PyExpression receiver,
                                       @NotNull TypeEvalContext context,
                                       boolean allowCoroutineOrGenerator)