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); }
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; } } } }