@Nullable
  private KotlinType checkConventionForIterator(
      @NotNull ExpressionTypingContext context,
      @NotNull KtExpression loopRangeExpression,
      @NotNull KotlinType iteratorType,
      @NotNull String name,
      @NotNull DiagnosticFactory1<KtExpression, KotlinType> ambiguity,
      @NotNull DiagnosticFactory1<KtExpression, KotlinType> missing,
      @NotNull DiagnosticFactory1<KtExpression, KotlinType> noneApplicable,
      @NotNull WritableSlice<KtExpression, ResolvedCall<FunctionDescriptor>> resolvedCallKey) {
    OverloadResolutionResults<FunctionDescriptor> nextResolutionResults =
        fakeCallResolver.resolveFakeCall(
            context,
            new TransientReceiver(iteratorType),
            Name.identifier(name),
            loopRangeExpression);
    if (nextResolutionResults.isAmbiguity()) {
      context.trace.report(ambiguity.on(loopRangeExpression, iteratorType));
    } else if (nextResolutionResults.isNothing()) {
      context.trace.report(missing.on(loopRangeExpression, iteratorType));
    } else if (!nextResolutionResults.isSuccess()) {
      context.trace.report(noneApplicable.on(loopRangeExpression, iteratorType));
    } else {
      assert nextResolutionResults.isSuccess();
      ResolvedCall<FunctionDescriptor> resolvedCall = nextResolutionResults.getResultingCall();
      context.trace.record(resolvedCallKey, loopRangeExpression, resolvedCall);
      FunctionDescriptor functionDescriptor = resolvedCall.getResultingDescriptor();
      symbolUsageValidator.validateCall(
          resolvedCall, functionDescriptor, context.trace, loopRangeExpression);

      checkIfOperatorModifierPresent(loopRangeExpression, functionDescriptor, context.trace);

      return functionDescriptor.getReturnType();
    }
    return null;
  }
  @Nullable
  public KotlinType checkIterableConvention(
      @NotNull ExpressionReceiver loopRange, ExpressionTypingContext context) {
    KtExpression loopRangeExpression = loopRange.getExpression();

    // Make a fake call loopRange.iterator(), and try to resolve it
    Name iterator = Name.identifier("iterator");
    Pair<Call, OverloadResolutionResults<FunctionDescriptor>> calls =
        fakeCallResolver.makeAndResolveFakeCall(
            loopRange,
            context,
            Collections.<KtExpression>emptyList(),
            iterator,
            loopRangeExpression,
            FakeCallKind.ITERATOR,
            loopRangeExpression);
    OverloadResolutionResults<FunctionDescriptor> iteratorResolutionResults = calls.getSecond();

    if (iteratorResolutionResults.isSuccess()) {
      ResolvedCall<FunctionDescriptor> iteratorResolvedCall =
          iteratorResolutionResults.getResultingCall();
      context.trace.record(
          LOOP_RANGE_ITERATOR_RESOLVED_CALL, loopRangeExpression, iteratorResolvedCall);
      FunctionDescriptor iteratorFunction = iteratorResolvedCall.getResultingDescriptor();

      checkIfOperatorModifierPresent(loopRangeExpression, iteratorFunction, context.trace);

      symbolUsageValidator.validateCall(
          iteratorResolvedCall, iteratorFunction, context.trace, loopRangeExpression);

      KotlinType iteratorType = iteratorFunction.getReturnType();
      KotlinType hasNextType =
          checkConventionForIterator(
              context,
              loopRangeExpression,
              iteratorType,
              "hasNext",
              HAS_NEXT_FUNCTION_AMBIGUITY,
              HAS_NEXT_MISSING,
              HAS_NEXT_FUNCTION_NONE_APPLICABLE,
              LOOP_RANGE_HAS_NEXT_RESOLVED_CALL);
      if (hasNextType != null && !builtIns.isBooleanOrSubtype(hasNextType)) {
        context.trace.report(HAS_NEXT_FUNCTION_TYPE_MISMATCH.on(loopRangeExpression, hasNextType));
      }
      return checkConventionForIterator(
          context,
          loopRangeExpression,
          iteratorType,
          "next",
          NEXT_AMBIGUITY,
          NEXT_MISSING,
          NEXT_NONE_APPLICABLE,
          LOOP_RANGE_NEXT_RESOLVED_CALL);
    } else {
      if (iteratorResolutionResults.isAmbiguity()) {
        context.trace.report(
            ITERATOR_AMBIGUITY.on(
                loopRangeExpression, iteratorResolutionResults.getResultingCalls()));
      } else {
        context.trace.report(ITERATOR_MISSING.on(loopRangeExpression));
      }
    }
    return null;
  }