@NotNull
  protected JetTypeInfo visitAssignmentOperation(
      JetBinaryExpression expression, ExpressionTypingContext contextWithExpectedType) {
    // There is a temporary binding trace for an opportunity to resolve set method for array if
    // needed (the initial trace should be used there)
    TemporaryBindingTrace temporaryBindingTrace =
        TemporaryBindingTrace.create(
            contextWithExpectedType.trace,
            "trace to resolve array set method for binary expression",
            expression);
    ExpressionTypingContext context =
        contextWithExpectedType
            .replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE)
            .replaceBindingTrace(temporaryBindingTrace);

    JetSimpleNameExpression operationSign = expression.getOperationReference();
    IElementType operationType = operationSign.getReferencedNameElementType();
    JetTypeInfo leftInfo = facade.getTypeInfo(expression.getLeft(), context);
    JetType leftType = leftInfo.getType();
    DataFlowInfo dataFlowInfo = leftInfo.getDataFlowInfo();

    JetExpression right = expression.getRight();
    JetExpression left = JetPsiUtil.deparenthesizeWithNoTypeResolution(expression.getLeft());
    if (right == null || left == null) {
      temporaryBindingTrace.commit();
      return JetTypeInfo.create(null, dataFlowInfo);
    }

    if (leftType == null) {
      dataFlowInfo =
          facade.getTypeInfo(right, context.replaceDataFlowInfo(dataFlowInfo)).getDataFlowInfo();
      context.trace.report(UNRESOLVED_REFERENCE.on(operationSign));
      temporaryBindingTrace.commit();
      return JetTypeInfo.create(null, dataFlowInfo);
    }
    ExpressionReceiver receiver = new ExpressionReceiver(left, leftType);

    // We check that defined only one of '+=' and '+' operations, and call it (in the case '+' we
    // then also assign)
    // Check for '+='
    Name name = OperatorConventions.ASSIGNMENT_OPERATIONS.get(operationType);
    TemporaryBindingTrace assignmentOperationTrace =
        TemporaryBindingTrace.create(
            context.trace, "trace to check assignment operation like '+=' for", expression);
    OverloadResolutionResults<FunctionDescriptor> assignmentOperationDescriptors =
        basic.getResolutionResultsForBinaryCall(
            scope,
            name,
            context.replaceBindingTrace(assignmentOperationTrace),
            expression,
            receiver);
    JetType assignmentOperationType =
        OverloadResolutionResultsUtil.getResultType(assignmentOperationDescriptors);

    // Check for '+'
    Name counterpartName =
        OperatorConventions.BINARY_OPERATION_NAMES.get(
            OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS.get(operationType));
    TemporaryBindingTrace binaryOperationTrace =
        TemporaryBindingTrace.create(
            context.trace, "trace to check binary operation like '+' for", expression);
    OverloadResolutionResults<FunctionDescriptor> binaryOperationDescriptors =
        basic.getResolutionResultsForBinaryCall(
            scope,
            counterpartName,
            context.replaceBindingTrace(binaryOperationTrace),
            expression,
            receiver);
    JetType binaryOperationType =
        OverloadResolutionResultsUtil.getResultType(binaryOperationDescriptors);

    JetType type = assignmentOperationType != null ? assignmentOperationType : binaryOperationType;
    if (assignmentOperationType != null && binaryOperationType != null) {
      OverloadResolutionResults<FunctionDescriptor> ambiguityResolutionResults =
          OverloadResolutionResultsUtil.ambiguity(
              assignmentOperationDescriptors, binaryOperationDescriptors);
      context.trace.report(
          ASSIGN_OPERATOR_AMBIGUITY.on(
              operationSign, ambiguityResolutionResults.getResultingCalls()));
      Collection<DeclarationDescriptor> descriptors = Sets.newHashSet();
      for (ResolvedCall<? extends FunctionDescriptor> call :
          ambiguityResolutionResults.getResultingCalls()) {
        descriptors.add(call.getResultingDescriptor());
      }
      dataFlowInfo =
          facade.getTypeInfo(right, context.replaceDataFlowInfo(dataFlowInfo)).getDataFlowInfo();
      context.trace.record(AMBIGUOUS_REFERENCE_TARGET, operationSign, descriptors);
    } else if (assignmentOperationType != null) {
      assignmentOperationTrace.commit();
      if (!KotlinBuiltIns.getInstance().isUnit(assignmentOperationType)) {
        context.trace.report(
            ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT.on(
                operationSign,
                assignmentOperationDescriptors.getResultingDescriptor(),
                operationSign));
      }
    } else {
      binaryOperationTrace.commit();
      context.trace.record(VARIABLE_REASSIGNMENT, expression);
      if (left instanceof JetArrayAccessExpression) {
        ExpressionTypingContext contextForResolve =
            context
                .replaceScope(scope)
                .replaceBindingTrace(
                    TemporaryBindingTrace.create(
                        contextWithExpectedType.trace,
                        "trace to resolve array set method for assignment",
                        expression));
        basic.resolveArrayAccessSetMethod(
            (JetArrayAccessExpression) left, right, contextForResolve, context.trace);
      }
      dataFlowInfo =
          facade.getTypeInfo(right, context.replaceDataFlowInfo(dataFlowInfo)).getDataFlowInfo();
    }
    basic.checkLValue(context.trace, expression.getLeft());
    temporaryBindingTrace.commit();
    return JetTypeInfo.create(
        checkAssignmentType(type, expression, contextWithExpectedType), dataFlowInfo);
  }
  private void storeResolutionResult(
      @NotNull Collection<? extends DeclarationDescriptor> descriptors,
      @NotNull Collection<? extends DeclarationDescriptor> canBeImportedDescriptors,
      @NotNull JetSimpleNameExpression referenceExpression,
      @NotNull Collection<JetScope> possibleResolutionScopes,
      @NotNull BindingTrace trace,
      @NotNull JetScope scopeToCheckVisibility) {

    assert canBeImportedDescriptors.size() <= descriptors.size();
    assert !possibleResolutionScopes.isEmpty();
    // todo completion here needs all possible resolution scopes, if there are many
    JetScope resolutionScope = possibleResolutionScopes.iterator().next();

    // A special case - will fill all trace information
    if (resolveClassNamespaceAmbiguity(
        canBeImportedDescriptors,
        referenceExpression,
        resolutionScope,
        trace,
        scopeToCheckVisibility)) {
      return;
    }

    // Simple case of no descriptors
    if (descriptors.isEmpty()) {
      trace.record(BindingContext.RESOLUTION_SCOPE, referenceExpression, resolutionScope);
      trace.report(UNRESOLVED_REFERENCE.on(referenceExpression));
      return;
    }

    // Decide if expression has resolved reference
    DeclarationDescriptor descriptor = null;
    if (descriptors.size() == 1) {
      descriptor = descriptors.iterator().next();
      assert canBeImportedDescriptors.size() <= 1;
    } else if (canBeImportedDescriptors.size() == 1) {
      descriptor = canBeImportedDescriptors.iterator().next();
    }
    if (descriptor != null) {
      trace.record(
          BindingContext.REFERENCE_TARGET, referenceExpression, descriptors.iterator().next());
      trace.record(BindingContext.RESOLUTION_SCOPE, referenceExpression, resolutionScope);

      if (descriptor instanceof DeclarationDescriptorWithVisibility) {
        checkVisibility(
            (DeclarationDescriptorWithVisibility) descriptor,
            trace,
            referenceExpression,
            scopeToCheckVisibility);
      }
    }

    // Check for more information and additional errors
    if (canBeImportedDescriptors.isEmpty()) {
      assert descriptors.size() >= 1;
      trace.report(CANNOT_BE_IMPORTED.on(referenceExpression, descriptors.iterator().next()));
      return;
    }
    if (canBeImportedDescriptors.size() > 1) {
      trace.record(BindingContext.AMBIGUOUS_REFERENCE_TARGET, referenceExpression, descriptors);
    }
  }
Example #3
0
  @NotNull
  private <D extends CallableDescriptor, F extends D>
      OverloadResolutionResultsImpl<F> doResolveCall(
          @NotNull BasicCallResolutionContext context,
          @NotNull List<ResolutionTask<D, F>> prioritizedTasks, // high to low priority
          @NotNull CallTransformer<D, F> callTransformer,
          @NotNull JetReferenceExpression reference) {

    ResolutionDebugInfo.Data debugInfo = ResolutionDebugInfo.create();
    context.trace.record(
        ResolutionDebugInfo.RESOLUTION_DEBUG_INFO, context.call.getCallElement(), debugInfo);
    context.trace.record(RESOLUTION_SCOPE, context.call.getCalleeExpression(), context.scope);

    if (context.dataFlowInfo.hasTypeInfoConstraints()) {
      context.trace.record(
          NON_DEFAULT_EXPRESSION_DATA_FLOW,
          context.call.getCalleeExpression(),
          context.dataFlowInfo);
    }

    debugInfo.set(ResolutionDebugInfo.TASKS, prioritizedTasks);

    if (context.checkArguments == CheckValueArgumentsMode.ENABLED) {
      argumentTypeResolver.analyzeArgumentsAndRecordTypes(context);
    }

    TemporaryBindingTrace traceForFirstNonemptyCandidateSet = null;
    OverloadResolutionResultsImpl<F> resultsForFirstNonemptyCandidateSet = null;
    for (ResolutionTask<D, F> task : prioritizedTasks) {
      TemporaryBindingTrace taskTrace =
          TemporaryBindingTrace.create(
              context.trace, "trace to resolve a task for", task.reference);
      OverloadResolutionResultsImpl<F> results =
          performResolutionGuardedForExtraFunctionLiteralArguments(
              task.replaceBindingTrace(taskTrace), callTransformer);
      if (results.isSuccess() || results.isAmbiguity()) {
        taskTrace.commit();

        if (results.isSuccess()) {
          debugInfo.set(ResolutionDebugInfo.RESULT, results.getResultingCall());
        }

        resolveFunctionArguments(context, results);
        return results;
      }
      if (results.getResultCode() == INCOMPLETE_TYPE_INFERENCE) {
        results.setTrace(taskTrace);
        return results;
      }
      boolean updateResults =
          traceForFirstNonemptyCandidateSet == null
              || (resultsForFirstNonemptyCandidateSet.getResultCode()
                      == CANDIDATES_WITH_WRONG_RECEIVER
                  && results.getResultCode() != CANDIDATES_WITH_WRONG_RECEIVER);
      if (!task.getCandidates().isEmpty() && !results.isNothing() && updateResults) {
        traceForFirstNonemptyCandidateSet = taskTrace;
        resultsForFirstNonemptyCandidateSet = results;
      }
    }
    if (traceForFirstNonemptyCandidateSet != null) {
      traceForFirstNonemptyCandidateSet.commit();
      if (resultsForFirstNonemptyCandidateSet.isSingleResult()) {

        debugInfo.set(
            ResolutionDebugInfo.RESULT, resultsForFirstNonemptyCandidateSet.getResultingCall());
      }
      resolveFunctionArguments(context, resultsForFirstNonemptyCandidateSet);
    } else {
      context.trace.report(UNRESOLVED_REFERENCE.on(reference, reference));
      argumentTypeResolver.checkTypesWithNoCallee(context, RESOLVE_FUNCTION_ARGUMENTS);
    }
    return resultsForFirstNonemptyCandidateSet != null
        ? resultsForFirstNonemptyCandidateSet
        : OverloadResolutionResultsImpl.<F>nameNotFound();
  }