@NotNull
  public TypeInfoForCall getQualifiedExpressionExtendedTypeInfo(
      @NotNull JetQualifiedExpression expression,
      @NotNull ResolutionContext context,
      @NotNull ResolveMode resolveMode) {
    // TODO : functions as values
    JetExpression selectorExpression = expression.getSelectorExpression();
    JetExpression receiverExpression = expression.getReceiverExpression();
    JetTypeInfo receiverTypeInfo =
        expressionTypingServices.getTypeInfoWithNamespaces(
            receiverExpression,
            context.scope,
            NO_EXPECTED_TYPE,
            context.dataFlowInfo,
            context.trace);
    JetType receiverType = receiverTypeInfo.getType();
    if (selectorExpression == null) return TypeInfoForCall.create(null, context.dataFlowInfo);
    if (receiverType == null)
      receiverType = ErrorUtils.createErrorType("Type for " + expression.getText());

    context = context.replaceDataFlowInfo(receiverTypeInfo.getDataFlowInfo());

    if (selectorExpression instanceof JetSimpleNameExpression) {
      ConstantUtils.propagateConstantValues(
          expression, context.trace, (JetSimpleNameExpression) selectorExpression);
    }

    TypeInfoForCall selectorReturnTypeInfo =
        getSelectorReturnTypeInfo(
            new ExpressionReceiver(receiverExpression, receiverType),
            expression.getOperationTokenNode(),
            selectorExpression,
            context,
            resolveMode);
    JetType selectorReturnType = selectorReturnTypeInfo.getType();

    // TODO move further
    if (!(receiverType instanceof NamespaceType)
        && expression.getOperationSign() == JetTokens.SAFE_ACCESS) {
      if (selectorReturnType != null
          && !selectorReturnType.isNullable()
          && !KotlinBuiltIns.getInstance().isUnit(selectorReturnType)) {
        if (receiverType.isNullable()) {
          selectorReturnType = TypeUtils.makeNullable(selectorReturnType);
        }
      }
    }

    // TODO : this is suspicious: remove this code?
    if (selectorReturnType != null) {
      context.trace.record(BindingContext.EXPRESSION_TYPE, selectorExpression, selectorReturnType);
    }
    JetTypeInfo typeInfo =
        JetTypeInfo.create(selectorReturnType, selectorReturnTypeInfo.getDataFlowInfo());
    if (resolveMode == ResolveMode.TOP_LEVEL_CALL) {
      DataFlowUtils.checkType(typeInfo.getType(), expression, context, typeInfo.getDataFlowInfo());
    }
    return TypeInfoForCall.create(typeInfo, selectorReturnTypeInfo);
  }
 @NotNull
 private JetTypeInfo getSelectorReturnTypeInfo(
     @NotNull ReceiverValue receiver,
     @Nullable ASTNode callOperationNode,
     @NotNull JetExpression selectorExpression,
     @NotNull ResolutionContext context,
     @NotNull ResolveMode resolveMode,
     @NotNull ResolutionResultsCache resolutionResultsCache) {
   if (selectorExpression instanceof JetCallExpression) {
     return getCallExpressionTypeInfoWithoutFinalTypeCheck(
         (JetCallExpression) selectorExpression,
         receiver,
         callOperationNode,
         context,
         resolveMode,
         resolutionResultsCache);
   } else if (selectorExpression instanceof JetSimpleNameExpression) {
     return getSimpleNameExpressionTypeInfo(
         (JetSimpleNameExpression) selectorExpression, receiver, callOperationNode, context);
   } else if (selectorExpression instanceof JetQualifiedExpression) {
     JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) selectorExpression;
     JetExpression newReceiverExpression = qualifiedExpression.getReceiverExpression();
     JetTypeInfo newReceiverTypeInfo =
         getSelectorReturnTypeInfo(
             receiver,
             callOperationNode,
             newReceiverExpression,
             context.replaceExpectedType(NO_EXPECTED_TYPE),
             resolveMode,
             resolutionResultsCache);
     JetType newReceiverType = newReceiverTypeInfo.getType();
     DataFlowInfo newReceiverDataFlowInfo = newReceiverTypeInfo.getDataFlowInfo();
     JetExpression newSelectorExpression = qualifiedExpression.getSelectorExpression();
     if (newReceiverType != null && newSelectorExpression != null) {
       ExpressionReceiver expressionReceiver =
           new ExpressionReceiver(newReceiverExpression, newReceiverType);
       return getSelectorReturnTypeInfo(
           expressionReceiver,
           qualifiedExpression.getOperationTokenNode(),
           newSelectorExpression,
           context.replaceDataFlowInfo(newReceiverDataFlowInfo),
           resolveMode,
           resolutionResultsCache);
     }
   } else {
     context.trace.report(ILLEGAL_SELECTOR.on(selectorExpression, selectorExpression.getText()));
   }
   return JetTypeInfo.create(null, context.dataFlowInfo);
 }
 @NotNull
 public <D extends CallableDescriptor> JetTypeInfo getCallExpressionTypeInfo(
     @NotNull JetCallExpression callExpression,
     @NotNull ReceiverValue receiver,
     @Nullable ASTNode callOperationNode,
     @NotNull ResolutionContext context,
     @NotNull ResolveMode resolveMode,
     @NotNull ResolutionResultsCache resolutionResultsCache) {
   JetTypeInfo typeInfo =
       getCallExpressionTypeInfoWithoutFinalTypeCheck(
           callExpression,
           receiver,
           callOperationNode,
           context,
           resolveMode,
           resolutionResultsCache);
   if (resolveMode == ResolveMode.TOP_LEVEL_CALL) {
     DataFlowUtils.checkType(
         typeInfo.getType(), callExpression, context, typeInfo.getDataFlowInfo());
   }
   return typeInfo;
 }