private static boolean matchProtocols(@NotNull PyClassType expected, @NotNull PyClassType actual, @NotNull MatchContext matchContext) {
    GenericSubstitutions substitutions = collectTypeSubstitutions(actual, matchContext.context);

    MatchContext protocolContext = new MatchContext(matchContext.context, new GenericSubstitutions(), matchContext.reversedSubstitutions);
    for (kotlin.Pair<PyTypedElement, List<RatedResolveResult>> pair : PyProtocolsKt.inspectProtocolSubclass(expected, actual, matchContext.context)) {
      final List<RatedResolveResult> subclassElements = pair.getSecond();
      if (ContainerUtil.isEmpty(subclassElements)) {
        return false;
      }

      final PyType protocolElementType = dropSelfIfNeeded(expected, matchContext.context.getType(pair.getFirst()), matchContext.context);
      final boolean elementResult = StreamEx
        .of(subclassElements)
        .map(ResolveResult::getElement)
        .select(PyTypedElement.class)
        .map(matchContext.context::getType)
        .map(type -> dropSelfIfNeeded(actual, type, matchContext.context))
        .map(type -> substitute(type, substitutions, matchContext.context))
        .anyMatch(
          subclassElementType -> {
            boolean matched = match(protocolElementType, subclassElementType, protocolContext).orElse(true);
            if (!matched) return false;
            if (!(protocolElementType instanceof PyFunctionType) || !(subclassElementType instanceof PyFunctionType)) return matched;
            var protocolReturnType = ((PyFunctionType)protocolElementType).getReturnType(protocolContext.context);
            if (protocolReturnType instanceof PySelfType) {
              var subclassReturnType = ((PyFunctionType)subclassElementType).getReturnType(protocolContext.context);
              if (subclassReturnType instanceof PySelfType) return true;
              return match(actual, subclassReturnType, matchContext).orElse(true);
            }
            return matched;
          }
        );

      if (!elementResult) {
        return false;
      }
    }


    if (expected instanceof PyCollectionType) {
      PyCollectionType genericSuperClass = findGenericDefinitionType(expected.getPyClass(), matchContext.context);
      if (genericSuperClass != null) {
        PyCollectionType concreteSuperClass = (PyCollectionType)substitute(genericSuperClass, protocolContext.mySubstitutions, protocolContext.context);
        assert concreteSuperClass != null;
        return matchGenericClassesParameterWise((PyCollectionType)expected, concreteSuperClass, matchContext);
      }
    }

    return true;
  }
private static boolean matchProtocols(@NotNull PyClassType expected, @NotNull PyClassType actual, @NotNull MatchContext matchContext)