private void AnalyzeOperationBlock(
            OperationBlockAnalysisContext operationBlockContext,
            DisposeAnalysisHelper disposeAnalysisHelper,
            ConcurrentDictionary <Location, bool> reportedLocations)
        {
            // We are only intersted in analyzing method bodies that have at least one disposable object creation.
            if (!(operationBlockContext.OwningSymbol is IMethodSymbol containingMethod) ||
                !disposeAnalysisHelper.HasAnyDisposableCreationDescendant(operationBlockContext.OperationBlocks, containingMethod))
            {
                return;
            }

            PerformFlowAnalysisOnOperationBlock(operationBlockContext, disposeAnalysisHelper, reportedLocations, containingMethod);
        }
Esempio n. 2
0
            private void OnMethodOperationBlockStart(OperationBlockStartAnalysisContext operationBlockStartContext, IMethodSymbol containingMethod)
            {
                // Shared PointsTo dataflow analysis result for all the callbacks to AnalyzeFieldReference
                // for this method's executable code.
                PointsToAnalysisResult lazyPointsToAnalysisResult = null;

                // If we have any potential disposable object creation descendant within the operation blocks,
                // register an operation action to analyze field references where field might be assigned a disposable object.
                if (_disposeAnalysisHelper.HasAnyDisposableCreationDescendant(operationBlockStartContext.OperationBlocks, containingMethod))
                {
                    operationBlockStartContext.RegisterOperationAction(AnalyzeFieldReference, OperationKind.FieldReference);
                }

                // If this is a Dispose method, then analyze dispose invocations for fields within this method.
                if (_disposeAnalysisHelper.IsAnyDisposeMethod(containingMethod))
                {
                    AnalyzeDisposeMethod();
                }

                return;

                // Local function
                void AnalyzeFieldReference(OperationAnalysisContext operationContext)
                {
                    var fieldReference = (IFieldReferenceOperation)operationContext.Operation;
                    var field          = fieldReference.Field;

                    // Check if this is a Disposable field that is not currently being tracked.
                    if (_fieldDisposeValueMap.ContainsKey(field) ||
                        !_disposableFields.Contains(field) ||
                        _hasErrors)
                    {
                        return;
                    }

                    // Only track instance fields on the current instance.
                    if (field.IsStatic || fieldReference.Instance?.Kind != OperationKind.InstanceReference)
                    {
                        return;
                    }

                    // We have a field reference for a disposable field.
                    // Check if it is being assigned a locally created disposable object.
                    if (fieldReference.Parent is ISimpleAssignmentOperation simpleAssignmentOperation &&
                        simpleAssignmentOperation.Target == fieldReference)
                    {
                        if (lazyPointsToAnalysisResult == null)
                        {
                            if (_disposeAnalysisHelper.TryGetOrComputeResult(
                                    operationBlockStartContext, containingMethod,
                                    s_disposableFieldsShouldBeDisposedRule,
                                    trackInstanceFields: false,
                                    out _, out var pointsToAnalysisResult) &&
                                pointsToAnalysisResult != null)
                            {
                                Interlocked.CompareExchange(ref lazyPointsToAnalysisResult, pointsToAnalysisResult, null);
                            }
                            else
                            {
                                _hasErrors = true;
                                return;
                            }
                        }

                        PointsToAbstractValue assignedPointsToValue = lazyPointsToAnalysisResult[simpleAssignmentOperation.Value.Kind, simpleAssignmentOperation.Value.Syntax];
                        foreach (var location in assignedPointsToValue.Locations)
                        {
                            if (_disposeAnalysisHelper.IsDisposableCreationOrDisposeOwnershipTransfer(location, containingMethod))
                            {
                                AddOrUpdateFieldDisposedValue(field, disposed: false);
                                break;
                            }
                        }
                    }
                }

                void AnalyzeDisposeMethod()
                {
                    if (_hasErrors)
                    {
                        return;
                    }

                    // Perform dataflow analysis to compute dispose value of disposable fields at the end of dispose method.
                    if (_disposeAnalysisHelper.TryGetOrComputeResult(operationBlockStartContext, containingMethod,
                                                                     s_disposableFieldsShouldBeDisposedRule, trackInstanceFields: true,
                                                                     disposeAnalysisResult: out var disposeAnalysisResult,
                                                                     pointsToAnalysisResult: out var pointsToAnalysisResult))
                    {
                        BasicBlock exitBlock = disposeAnalysisResult.ControlFlowGraph.GetExit();
                        foreach (var fieldWithPointsToValue in disposeAnalysisResult.TrackedInstanceFieldPointsToMap)
                        {
                            IFieldSymbol          field         = fieldWithPointsToValue.Key;
                            PointsToAbstractValue pointsToValue = fieldWithPointsToValue.Value;

                            if (!_disposableFields.Contains(field))
                            {
                                continue;
                            }

                            ImmutableDictionary <AbstractLocation, DisposeAbstractValue> disposeDataAtExit = disposeAnalysisResult.ExitBlockOutput.Data;
                            var disposed = false;
                            foreach (var location in pointsToValue.Locations)
                            {
                                if (disposeDataAtExit.TryGetValue(location, out DisposeAbstractValue disposeValue))
                                {
                                    switch (disposeValue.Kind)
                                    {
                                    // For MaybeDisposed, conservatively mark the field as disposed as we don't support path sensitive analysis.
                                    case DisposeAbstractValueKind.MaybeDisposed:
                                    case DisposeAbstractValueKind.Unknown:
                                    case DisposeAbstractValueKind.Escaped:
                                    case DisposeAbstractValueKind.Disposed:
                                        disposed = true;
                                        AddOrUpdateFieldDisposedValue(field, disposed);
                                        break;
                                    }
                                }

                                if (disposed)
                                {
                                    break;
                                }
                            }
                        }
                    }