Exemple #1
0
        public override void Initialize(AnalysisContext context)
        {
            context.EnableConcurrentExecution();
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
            context.RegisterCompilationStartAction(compilationContext =>
            {
                if (!DisposeAnalysisHelper.TryGetOrCreate(compilationContext.Compilation, out DisposeAnalysisHelper disposeAnalysisHelper))
                {
                    return;
                }

                compilationContext.RegisterOperationBlockAction(operationBlockContext =>
                {
                    if (!(operationBlockContext.OwningSymbol is IMethodSymbol containingMethod) ||
                        !disposeAnalysisHelper.HasAnyDisposableCreationDescendant(operationBlockContext.OperationBlocks, containingMethod))
                    {
                        return;
                    }

                    DataFlowAnalysisResult <DisposeBlockAnalysisResult, DisposeAbstractValue> disposeAnalysisResult;
                    if (disposeAnalysisHelper.TryGetOrComputeResult(operationBlockContext.OperationBlocks, containingMethod, out disposeAnalysisResult))
                    {
                        BasicBlock exitBlock = disposeAnalysisResult.ControlFlowGraph.Exit;
                        ImmutableDictionary <AbstractLocation, DisposeAbstractValue> disposeDataAtExit = disposeAnalysisResult[exitBlock].OutputData;
                        foreach (var kvp in disposeDataAtExit)
                        {
                            AbstractLocation location         = kvp.Key;
                            DisposeAbstractValue disposeValue = kvp.Value;
                            if (disposeValue.Kind == DisposeAbstractValueKind.NotDisposable ||
                                location.CreationOpt == null)
                            {
                                continue;
                            }

                            if (disposeValue.Kind == DisposeAbstractValueKind.NotDisposed ||
                                (disposeValue.DisposingOrEscapingOperations.Count > 0 &&
                                 disposeValue.DisposingOrEscapingOperations.All(d => d.IsInsideCatchClause())))
                            {
                                // CA2000: In method '{0}', call System.IDisposable.Dispose on object created by '{1}' before all references to it are out of scope.
                                var arg1       = containingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
                                var arg2       = location.CreationOpt.Syntax.ToString();
                                var diagnostic = location.CreationOpt.Syntax.CreateDiagnostic(Rule, arg1, arg2);
                                operationBlockContext.ReportDiagnostic(diagnostic);
                            }
                        }
                    }
                });
            });
        }
        public override void Initialize(AnalysisContext context)
        {
            context.EnableConcurrentExecution();
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);

            context.RegisterCompilationStartAction(compilationContext =>
            {
                if (!DisposeAnalysisHelper.TryGetOrCreate(compilationContext.Compilation, out DisposeAnalysisHelper disposeAnalysisHelper))
                {
                    return;
                }

                var fieldDisposeValueMap = new ConcurrentDictionary<IFieldSymbol, /*disposed*/bool>();
                void addOrUpdateFieldDisposedValue(IFieldSymbol field, bool disposed)
                {
                    Debug.Assert(!field.IsStatic);
                    Debug.Assert(field.Type.IsDisposable(disposeAnalysisHelper.IDisposable));

                    fieldDisposeValueMap.AddOrUpdate(field,
                        addValue: disposed,
                        updateValueFactory: (f, currentValue) => currentValue || disposed);
                };

                // Disposable fields with initializer at declaration must be disposed.
                compilationContext.RegisterOperationAction(operationContext =>
                {
                    var initializedFields = ((IFieldInitializerOperation)operationContext.Operation).InitializedFields;
                    foreach (var field in initializedFields)
                    {
                        if (!field.IsStatic &&
                            disposeAnalysisHelper.GetDisposableFields(field.ContainingType).Contains(field))
                        {
                            addOrUpdateFieldDisposedValue(field, disposed: false);
                        }
                    }
                },
                OperationKind.FieldInitializer);

                // Instance fields initialized in constructor/method body with a locally created disposable object must be disposed.
                compilationContext.RegisterOperationBlockStartAction(operationBlockStartContext =>
                {
                    if (!(operationBlockStartContext.OwningSymbol is IMethodSymbol containingMethod))
                    {
                        return;
                    }

                    if (disposeAnalysisHelper.HasAnyDisposableCreationDescendant(operationBlockStartContext.OperationBlocks, containingMethod))
                    {
                        if (disposeAnalysisHelper.TryGetOrComputeResult(operationBlockStartContext.OperationBlocks,
                            containingMethod, operationBlockStartContext.Options, Rule, trackInstanceFields: false,
                            trackExceptionPaths: false, operationBlockStartContext.CancellationToken,
                            out var disposeAnalysisResult, out var pointsToAnalysisResult))
                        {
                            Debug.Assert(disposeAnalysisResult != null);
                            Debug.Assert(pointsToAnalysisResult != null);

                            operationBlockStartContext.RegisterOperationAction(operationContext =>
                            {
                                var fieldReference = (IFieldReferenceOperation)operationContext.Operation;
                                var field = fieldReference.Field;

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

                                // Check if this is a Disposable field that is not currently being tracked.
                                if (fieldDisposeValueMap.ContainsKey(field) ||
                                    !disposeAnalysisHelper.GetDisposableFields(field.ContainingType).Contains(field))
                                {
                                    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)
                                {
                                    PointsToAbstractValue assignedPointsToValue = pointsToAnalysisResult[simpleAssignmentOperation.Value.Kind, simpleAssignmentOperation.Value.Syntax];
                                    foreach (var location in assignedPointsToValue.Locations)
                                    {
                                        if (disposeAnalysisHelper.IsDisposableCreationOrDisposeOwnershipTransfer(location, containingMethod))
                                        {
                                            addOrUpdateFieldDisposedValue(field, disposed: false);
                                            break;
                                        }
                                    }
                                }
                            },
                            OperationKind.FieldReference);
                        }
                    }

                    // Mark fields disposed in Dispose method(s).
                    if (containingMethod.GetDisposeMethodKind(disposeAnalysisHelper.IDisposable, disposeAnalysisHelper.Task) != DisposeMethodKind.None)
                    {
                        var disposableFields = disposeAnalysisHelper.GetDisposableFields(containingMethod.ContainingType);
                        if (!disposableFields.IsEmpty)
                        {
                            if (disposeAnalysisHelper.TryGetOrComputeResult(operationBlockStartContext.OperationBlocks, containingMethod,
                                operationBlockStartContext.Options, Rule, trackInstanceFields: true, trackExceptionPaths: false, cancellationToken: operationBlockStartContext.CancellationToken,
                                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;

                                    Debug.Assert(field.Type.IsDisposable(disposeAnalysisHelper.IDisposable));
                                    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;
                                        }
                                    }
                                }
                            }
                        }
                    }
                });

                compilationContext.RegisterCompilationEndAction(compilationEndContext =>
                {
                    foreach (var kvp in fieldDisposeValueMap)
                    {
                        IFieldSymbol field = kvp.Key;
                        bool disposed = kvp.Value;
                        if (!disposed)
                        {
                            // '{0}' contains field '{1}' that is of IDisposable type '{2}', but it is never disposed. Change the Dispose method on '{0}' to call Close or Dispose on this field.
                            var arg1 = field.ContainingType.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
                            var arg2 = field.Name;
                            var arg3 = field.Type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
                            var diagnostic = field.CreateDiagnostic(Rule, arg1, arg2, arg3);
                            compilationEndContext.ReportDiagnostic(diagnostic);
                        }
                    }
                });
            });
        }
Exemple #3
0
 protected DisposableFieldAnalyzer(Compilation compilation)
 {
     DisposeAnalysisHelper.TryGetOrCreate(compilation, out _disposeAnalysisHelper !);
     RoslynDebug.Assert(_disposeAnalysisHelper != null);
 }
        public override void Initialize(AnalysisContext context)
        {
            context.EnableConcurrentExecution();
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

            context.RegisterCompilationStartAction(compilationContext =>
            {
                if (!DisposeAnalysisHelper.TryGetOrCreate(compilationContext.Compilation, out DisposeAnalysisHelper? disposeAnalysisHelper))
                {
                    return;
                }

                compilationContext.RegisterOperationBlockStartAction(operationBlockStartContext =>
                {
                    if (!(operationBlockStartContext.OwningSymbol is IMethodSymbol containingMethod) ||
                        containingMethod.OverriddenMethod == null ||
                        containingMethod.OverriddenMethod.IsAbstract)
                    {
                        return;
                    }

                    var disposeMethodKind = disposeAnalysisHelper.GetDisposeMethodKind(containingMethod);
                    switch (disposeMethodKind)
                    {
                    case DisposeMethodKind.Dispose:
                    case DisposeMethodKind.DisposeBool:
                    case DisposeMethodKind.DisposeAsync:
                    case DisposeMethodKind.DisposeCoreAsync:
                        break;

                    case DisposeMethodKind.Close:
                        // FxCop compat: Ignore Close methods due to high false positive rate.
                        return;

                    default:
                        return;
                    }

                    var invokesBaseDispose = false;
                    operationBlockStartContext.RegisterOperationAction(operationContext =>
                    {
                        if (invokesBaseDispose)
                        {
                            return;
                        }

                        var invocation = (IInvocationOperation)operationContext.Operation;
                        if (Equals(invocation.TargetMethod, containingMethod.OverriddenMethod) &&
                            invocation.Instance is IInstanceReferenceOperation instanceReference &&
                            instanceReference.ReferenceKind == InstanceReferenceKind.ContainingTypeInstance)
                        {
                            Debug.Assert(disposeAnalysisHelper.GetDisposeMethodKind(invocation.TargetMethod) == disposeMethodKind);
                            invokesBaseDispose = true;
                        }
                    }, OperationKind.Invocation);

                    operationBlockStartContext.RegisterOperationBlockEndAction(operationEndContext =>
                    {
                        if (!invokesBaseDispose)
                        {
                            // Ensure that method '{0}' calls '{1}' in all possible control flow paths.
                            var arg1               = containingMethod.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
                            var baseKeyword        = containingMethod.Language == LanguageNames.CSharp ? "base" : "MyBase";
                            var disposeMethodParam = (disposeMethodKind == DisposeMethodKind.DisposeBool || disposeMethodKind == DisposeMethodKind.DisposeCoreAsync) ?
                                                     containingMethod.Language == LanguageNames.CSharp ? "bool" : "Boolean" :
                                                     string.Empty;
                            var disposeMethodName = disposeMethodKind == DisposeMethodKind.DisposeBool ?
                                                    "Dispose" :
                                                    disposeMethodKind.ToString();
                            var arg2       = $"{baseKeyword}.{disposeMethodName}({disposeMethodParam})";
                            var diagnostic = containingMethod.CreateDiagnostic(Rule, arg1, arg2);
                            operationEndContext.ReportDiagnostic(diagnostic);
                        }
                    });
                });
            });
        }
        public override void Initialize(AnalysisContext context)
        {
            context.EnableConcurrentExecution();
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);

            context.RegisterCompilationStartAction(compilationContext =>
            {
                if (!DisposeAnalysisHelper.TryGetOrCreate(compilationContext.Compilation, out DisposeAnalysisHelper? disposeAnalysisHelper))
                {
                    return;
                }

                var fieldDisposeValueMap = new ConcurrentDictionary <IFieldSymbol, /*disposed*/ bool>();
                void addOrUpdateFieldDisposedValue(IFieldSymbol field, bool disposed)
                {
                    Debug.Assert(!field.IsStatic);
                    Debug.Assert(disposeAnalysisHelper !.IsDisposable(field.Type));

                    fieldDisposeValueMap.AddOrUpdate(field,
                                                     addValue: disposed,
                                                     updateValueFactory: (f, currentValue) => currentValue || disposed);
                };

                var hasErrors = false;
                compilationContext.RegisterOperationAction(_ => hasErrors = true, OperationKind.Invalid);

                // Disposable fields with initializer at declaration must be disposed.
                compilationContext.RegisterOperationAction(operationContext =>
                {
                    if (!ShouldAnalyze(operationContext.ContainingSymbol.ContainingType))
                    {
                        return;
                    }

                    var initializedFields = ((IFieldInitializerOperation)operationContext.Operation).InitializedFields;
                    foreach (var field in initializedFields)
                    {
                        if (!field.IsStatic &&
                            disposeAnalysisHelper.GetDisposableFields(field.ContainingType).Contains(field))
                        {
                            addOrUpdateFieldDisposedValue(field, disposed: false);
                        }
                    }
                },
                                                           OperationKind.FieldInitializer);

                // Instance fields initialized in constructor/method body with a locally created disposable object must be disposed.
                compilationContext.RegisterOperationBlockStartAction(operationBlockStartContext =>
                {
                    if (!(operationBlockStartContext.OwningSymbol is IMethodSymbol containingMethod) ||
                        !ShouldAnalyze(containingMethod.ContainingType))
                    {
                        return;
                    }

                    if (disposeAnalysisHelper.HasAnyDisposableCreationDescendant(operationBlockStartContext.OperationBlocks, containingMethod))
                    {
                        PointsToAnalysisResult?lazyPointsToAnalysisResult = null;

                        operationBlockStartContext.RegisterOperationAction(operationContext =>
                        {
                            var fieldReference = (IFieldReferenceOperation)operationContext.Operation;
                            var field          = fieldReference.Field;

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

                            // Check if this is a Disposable field that is not currently being tracked.
                            if (fieldDisposeValueMap.ContainsKey(field) ||
                                !disposeAnalysisHelper.GetDisposableFields(field.ContainingType).Contains(field))
                            {
                                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)
                                {
                                    var cfg = operationBlockStartContext.OperationBlocks.GetControlFlowGraph();
                                    if (cfg == null)
                                    {
                                        hasErrors = true;
                                        return;
                                    }

                                    var wellKnownTypeProvider         = WellKnownTypeProvider.GetOrCreate(operationContext.Compilation);
                                    var interproceduralAnalysisConfig = InterproceduralAnalysisConfiguration.Create(
                                        operationBlockStartContext.Options, Rule, InterproceduralAnalysisKind.None, operationBlockStartContext.CancellationToken);
                                    var pointsToAnalysisResult = PointsToAnalysis.TryGetOrComputeResult(cfg,
                                                                                                        containingMethod, operationBlockStartContext.Options, wellKnownTypeProvider, interproceduralAnalysisConfig,
                                                                                                        interproceduralAnalysisPredicateOpt: null,
                                                                                                        pessimisticAnalysis: false, performCopyAnalysis: false);
                                    if (pointsToAnalysisResult == null)
                                    {
                                        hasErrors = true;
                                        return;
                                    }

                                    Interlocked.CompareExchange(ref lazyPointsToAnalysisResult, pointsToAnalysisResult, null);
                                }

                                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;
                                    }
                                }
                            }
                        },
                                                                           OperationKind.FieldReference);
                    }

                    // Mark fields disposed in Dispose method(s).
                    if (IsDisposeMethod(containingMethod))
                    {
                        var disposableFields = disposeAnalysisHelper.GetDisposableFields(containingMethod.ContainingType);
                        if (!disposableFields.IsEmpty)
                        {
                            if (disposeAnalysisHelper.TryGetOrComputeResult(operationBlockStartContext.OperationBlocks, containingMethod,
                                                                            operationBlockStartContext.Options, Rule, trackInstanceFields: true, trackExceptionPaths: false, cancellationToken: operationBlockStartContext.CancellationToken,
                                                                            disposeAnalysisResult: out var disposeAnalysisResult, pointsToAnalysisResult: out var pointsToAnalysisResult))
                            {
                                RoslynDebug.Assert(disposeAnalysisResult.TrackedInstanceFieldPointsToMap != null);

                                BasicBlock exitBlock = disposeAnalysisResult.ControlFlowGraph.GetExit();
                                foreach (var fieldWithPointsToValue in disposeAnalysisResult.TrackedInstanceFieldPointsToMap)
                                {
                                    IFieldSymbol field = fieldWithPointsToValue.Key;
                                    PointsToAbstractValue pointsToValue = fieldWithPointsToValue.Value;

                                    Debug.Assert(disposeAnalysisHelper.IsDisposable(field.Type));
                                    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;
                                        }
                                    }
                                }
                            }
                        }
                    }
                });

                compilationContext.RegisterCompilationEndAction(compilationEndContext =>
                {
                    if (hasErrors)
                    {
                        return;
                    }

                    foreach (var kvp in fieldDisposeValueMap)
                    {
                        IFieldSymbol field = kvp.Key;
                        bool disposed      = kvp.Value;

                        // Flag non-disposed fields only if the containing type has a Dispose method implementation.
                        if (!disposed &&
                            HasDisposeMethod(field.ContainingType))
                        {
                            // '{0}' contains field '{1}' that is of IDisposable type '{2}', but it is never disposed. Change the Dispose method on '{0}' to call Close or Dispose on this field.
                            var arg1       = field.ContainingType.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
                            var arg2       = field.Name;
                            var arg3       = field.Type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
                            var diagnostic = field.CreateDiagnostic(Rule, arg1, arg2, arg3);
                            compilationEndContext.ReportDiagnostic(diagnostic);
                        }
                    }
                });

                return;

                // Local functions
                bool ShouldAnalyze(INamedTypeSymbol namedType)
                {
                    // We only want to analyze types which are disposable (implement System.IDisposable directly or indirectly)
                    // and have at least one disposable field.
                    return(!hasErrors &&
                           disposeAnalysisHelper !.IsDisposable(namedType) &&
                           !disposeAnalysisHelper.GetDisposableFields(namedType).IsEmpty&&
                           !namedType.IsConfiguredToSkipAnalysis(compilationContext.Options, Rule, compilationContext.Compilation, compilationContext.CancellationToken));
                }

                bool IsDisposeMethod(IMethodSymbol method)
                => disposeAnalysisHelper !.GetDisposeMethodKind(method) != DisposeMethodKind.None;

                bool HasDisposeMethod(INamedTypeSymbol namedType)
                {
                    foreach (var method in namedType.GetMembers().OfType <IMethodSymbol>())
                    {
                        if (IsDisposeMethod(method))
                        {
                            return(true);
                        }
                    }

                    return(false);
                }
            });
        }
        public override void Initialize(AnalysisContext context)
        {
            context.EnableConcurrentExecution();
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
            context.RegisterCompilationStartAction(compilationContext =>
            {
                if (!DisposeAnalysisHelper.TryGetOrCreate(compilationContext.Compilation, out var disposeAnalysisHelper))
                {
                    return;
                }

                var reportedLocations = new ConcurrentDictionary <Location, bool>();
                compilationContext.RegisterOperationBlockAction(operationBlockContext =>
                {
                    if (!(operationBlockContext.OwningSymbol is IMethodSymbol containingMethod) ||
                        !disposeAnalysisHelper.HasAnyDisposableCreationDescendant(operationBlockContext.OperationBlocks, containingMethod) ||
                        containingMethod.IsConfiguredToSkipAnalysis(operationBlockContext.Options,
                                                                    NotDisposedRule, operationBlockContext.Compilation, operationBlockContext.CancellationToken))
                    {
                        return;
                    }

                    var disposeAnalysisKind = operationBlockContext.Options.GetDisposeAnalysisKindOption(NotDisposedOnExceptionPathsRule, containingMethod,
                                                                                                         operationBlockContext.Compilation, DisposeAnalysisKind.NonExceptionPaths, operationBlockContext.CancellationToken);
                    var trackExceptionPaths = disposeAnalysisKind.AreExceptionPathsEnabled();

                    // For non-exception paths analysis, we can skip interprocedural analysis for certain invocations.
                    var interproceduralAnalysisPredicateOpt = !trackExceptionPaths ?
                                                              new InterproceduralAnalysisPredicate(
                        skipAnalysisForInvokedMethodPredicateOpt: SkipInterproceduralAnalysis,
                        skipAnalysisForInvokedLambdaOrLocalFunctionPredicateOpt: null,
                        skipAnalysisForInvokedContextPredicateOpt: null) :
                                                              null;

                    if (disposeAnalysisHelper.TryGetOrComputeResult(operationBlockContext.OperationBlocks, containingMethod,
                                                                    operationBlockContext.Options, NotDisposedRule, trackInstanceFields: false, trackExceptionPaths,
                                                                    operationBlockContext.CancellationToken, out var disposeAnalysisResult, out var pointsToAnalysisResult,
                                                                    interproceduralAnalysisPredicateOpt))
                    {
                        var notDisposedDiagnostics      = ArrayBuilder <Diagnostic> .GetInstance();
                        var mayBeNotDisposedDiagnostics = ArrayBuilder <Diagnostic> .GetInstance();
                        try
                        {
                            // Compute diagnostics for undisposed objects at exit block for non-exceptional exit paths.
                            var exitBlock         = disposeAnalysisResult.ControlFlowGraph.GetExit();
                            var disposeDataAtExit = disposeAnalysisResult.ExitBlockOutput.Data;
                            ComputeDiagnostics(disposeDataAtExit,
                                               notDisposedDiagnostics, mayBeNotDisposedDiagnostics, disposeAnalysisResult, pointsToAnalysisResult,
                                               disposeAnalysisKind, isDisposeDataForExceptionPaths: false);

                            if (trackExceptionPaths)
                            {
                                // Compute diagnostics for undisposed objects at handled exception exit paths.
                                var disposeDataAtHandledExceptionPaths = disposeAnalysisResult.ExceptionPathsExitBlockOutputOpt !.Data;
                                ComputeDiagnostics(disposeDataAtHandledExceptionPaths,
                                                   notDisposedDiagnostics, mayBeNotDisposedDiagnostics, disposeAnalysisResult, pointsToAnalysisResult,
                                                   disposeAnalysisKind, isDisposeDataForExceptionPaths: true);

                                // Compute diagnostics for undisposed objects at unhandled exception exit paths, if any.
                                var disposeDataAtUnhandledExceptionPaths = disposeAnalysisResult.MergedStateForUnhandledThrowOperationsOpt?.Data;
                                if (disposeDataAtUnhandledExceptionPaths != null)
                                {
                                    ComputeDiagnostics(disposeDataAtUnhandledExceptionPaths,
                                                       notDisposedDiagnostics, mayBeNotDisposedDiagnostics, disposeAnalysisResult, pointsToAnalysisResult,
                                                       disposeAnalysisKind, isDisposeDataForExceptionPaths: true);
                                }
                            }

                            if (!notDisposedDiagnostics.Any() && !mayBeNotDisposedDiagnostics.Any())
                            {
                                return;
                            }

                            if (disposeAnalysisResult.ControlFlowGraph.OriginalOperation.HasAnyOperationDescendant(o => o.Kind == OperationKind.None && !(o.Parent is INameOfOperation)))
                            {
                                // Workaround for https://github.com/dotnet/roslyn/issues/32100
                                // Bail out in presence of OperationKind.None - not implemented IOperation.
                                return;
                            }

                            // Report diagnostics preferring *not* disposed diagnostics over may be not disposed diagnostics
                            // and avoiding duplicates.
                            foreach (var diagnostic in notDisposedDiagnostics.Concat(mayBeNotDisposedDiagnostics))
                            {
                                if (reportedLocations.TryAdd(diagnostic.Location, true))
                                {
                                    operationBlockContext.ReportDiagnostic(diagnostic);
                                }
                            }
                        }
                        finally
                        {
                            notDisposedDiagnostics.Free();
                            mayBeNotDisposedDiagnostics.Free();
                        }
                    }
                });

                return;

                // Local functions.

                bool SkipInterproceduralAnalysis(IMethodSymbol invokedMethod)
                {
                    // Skip interprocedural analysis if we are invoking a method and not passing any disposable object as an argument
                    // and not receiving a disposable object as a return value.
                    // We also check that we are not passing any object type argument which might hold disposable object
                    // and also check that we are not passing delegate type argument which can
                    // be a lambda or local function that has access to disposable object in current method's scope.

                    if (CanBeDisposable(invokedMethod.ReturnType))
                    {
                        return(false);
                    }

                    foreach (var p in invokedMethod.Parameters)
                    {
                        if (CanBeDisposable(p.Type))
                        {
                            return(false);
                        }
                    }

                    return(true);

                    bool CanBeDisposable(ITypeSymbol type)
                    => type.SpecialType == SpecialType.System_Object ||
                    type.DerivesFrom(disposeAnalysisHelper !.IDisposable) ||
                    type.TypeKind == TypeKind.Delegate;
                }
            });
        }