Skip to content

Incorrect nullability analysis in compound assignment #29962

@jcouv

Description

@jcouv
        public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
        {
            VisitLvalue(node.Left); // PROTOTYPE(NullableReferenceTypes): Method should be called VisitValue rather than VisitLvalue.
            TypeSymbolWithAnnotations leftType = _resultType;

            TypeSymbolWithAnnotations resultType;
            Debug.Assert(!IsConditionalState);

            //if (this.State.Reachable) // Consider reachability: see https://github.com/dotnet/roslyn/issues/28798
            {
                TypeSymbolWithAnnotations leftOnRightType = GetAdjustedResult(leftType, MakeSlot(node.Left));

                // PROTOTYPE(NullableReferenceTypes): Update operator based on inferred argument types.
                if ((object)node.Operator.LeftType != null)
                {
                    // PROTOTYPE(NullableReferenceTypes): Ignoring top-level nullability of operator left parameter.
                    leftOnRightType = ApplyConversion(
                        node.Left,
                        node.Left,
                        node.LeftConversion,
                        TypeSymbolWithAnnotations.Create(node.Operator.LeftType),
                        leftOnRightType,
                        checkConversion: true,
                        fromExplicitCast: false,
                        useLegacyWarnings: false,
                        AssignmentKind.Assignment,
                        reportTopLevelWarnings: false,
                        reportNestedWarnings: false);
                }
                else
                {
                    leftOnRightType = default;
                }

                VisitRvalue(node.Right);
                TypeSymbolWithAnnotations rightType = _resultType;

                if ((object)node.Operator.ReturnType != null)
                {
                    if (node.Operator.Kind.IsUserDefined() && (object)node.Operator.Method != null && node.Operator.Method.ParameterCount == 2)
                    {
                        ReportArgumentWarnings(node.Left, leftOnRightType, node.Operator.Method.Parameters[0]);
                        ReportArgumentWarnings(node.Right, rightType, node.Operator.Method.Parameters[1]);
                    }

                    resultType = InferResultNullability(node.Operator.Kind, node.Operator.Method, node.Operator.ReturnType, leftOnRightType, rightType);
                    resultType = ApplyConversion(
                        node,
                        node,
                        node.FinalConversion,
                        leftType,
                        resultType,
                        checkConversion: true,
                        fromExplicitCast: false,
                        useLegacyWarnings: false,
                        AssignmentKind.Assignment);
                }
                else
                {
                    resultType = TypeSymbolWithAnnotations.Create(node.Type);
                }

                TrackNullableStateForAssignment(node, leftType, MakeSlot(node.Left), resultType);
                _resultType = resultType;
            }
            //else
            //{
            //    VisitRvalue(node.Right);
            //    AfterRightHasBeenVisited(node);
            //    resultType = null;
            //}

            return null;
        }

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions