private static void ComputeDiagnostics(
            ImmutableDictionary <AbstractLocation, DisposeAbstractValue> disposeData,
            ArrayBuilder <Diagnostic> notDisposedDiagnostics,
            ArrayBuilder <Diagnostic> mayBeNotDisposedDiagnostics,
            DisposeAnalysisResult disposeAnalysisResult,
            PointsToAnalysisResult pointsToAnalysisResult,
            DisposeAnalysisKind disposeAnalysisKind,
            bool isDisposeDataForExceptionPaths)
        {
            foreach (var kvp in disposeData)
            {
                AbstractLocation     location     = kvp.Key;
                DisposeAbstractValue disposeValue = kvp.Value;
                if (disposeValue.Kind == DisposeAbstractValueKind.NotDisposable ||
                    location.CreationOpt == null)
                {
                    continue;
                }

                var isNotDisposed = disposeValue.Kind == DisposeAbstractValueKind.NotDisposed ||
                                    (disposeValue.DisposingOrEscapingOperations.Count > 0 &&
                                     disposeValue.DisposingOrEscapingOperations.All(d => d.IsInsideCatchRegion(disposeAnalysisResult.ControlFlowGraph)));
                var isMayBeNotDisposed = !isNotDisposed && (disposeValue.Kind == DisposeAbstractValueKind.MaybeDisposed || disposeValue.Kind == DisposeAbstractValueKind.NotDisposedOrEscaped);

                if (isNotDisposed ||
                    (isMayBeNotDisposed && disposeAnalysisKind.AreMayBeNotDisposedViolationsEnabled()))
                {
                    var syntax = location.TryGetNodeToReportDiagnostic(pointsToAnalysisResult);
                    if (syntax == null)
                    {
                        continue;
                    }

                    // CA2000: Call System.IDisposable.Dispose on object created by '{0}' before all references to it are out of scope.
                    var rule       = GetRule(isNotDisposed);
                    var argument   = syntax.ToString();
                    var diagnostic = syntax.CreateDiagnostic(rule, argument);
                    if (isNotDisposed)
                    {
                        notDisposedDiagnostics.Add(diagnostic);
                    }
                    else
                    {
                        mayBeNotDisposedDiagnostics.Add(diagnostic);
                    }
                }
            }

            DiagnosticDescriptor GetRule(bool isNotDisposed)
            {
                if (isNotDisposed)
                {
                    return(isDisposeDataForExceptionPaths ? NotDisposedOnExceptionPathsRule : NotDisposedRule);
                }
                else
                {
                    return(isDisposeDataForExceptionPaths ? MayBeDisposedOnExceptionPathsRule : MayBeDisposedRule);
                }
            }
        }
        public bool TryGetOrComputeResult(
            OperationBlockAnalysisContext context,
            IMethodSymbol containingMethod,
            DiagnosticDescriptor rule,
            InterproceduralAnalysisKind interproceduralAnalysisKind,
            bool trackInstanceFields,
            out DisposeAnalysisResult disposeAnalysisResult,
            out PointsToAnalysisResult pointsToAnalysisResult,
            InterproceduralAnalysisPredicate interproceduralAnalysisPredicateOpt = null)
        {
            // Compute the dispose analysis result - skip Attribute blocks (OperationKind.None)
            foreach (var operationBlock in context.OperationBlocks.Where(o => o.Kind != OperationKind.None))
            {
                var cfg = context.GetControlFlowGraph(operationBlock);
                if (cfg != null)
                {
                    var wellKnownTypeProvider = Analyzer.Utilities.WellKnownTypeProvider.GetOrCreate(context.Compilation);
                    disposeAnalysisResult = FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAnalysis.TryGetOrComputeResult(cfg, containingMethod, wellKnownTypeProvider,
                                                                                                                        context.Options, rule, _disposeOwnershipTransferLikelyTypes, trackInstanceFields,
                                                                                                                        exceptionPathsAnalysis: false, context.CancellationToken, out pointsToAnalysisResult,
                                                                                                                        interproceduralAnalysisKind,
                                                                                                                        interproceduralAnalysisPredicateOpt: interproceduralAnalysisPredicateOpt,
                                                                                                                        defaultDisposeOwnershipTransferAtConstructor: true,
                                                                                                                        defaultDisposeOwnershipTransferAtMethodCall: true);
                    if (disposeAnalysisResult != null)
                    {
                        return(true);
                    }
                }
            }

            disposeAnalysisResult  = null;
            pointsToAnalysisResult = null;
            return(false);
        }
        public bool TryGetOrComputeResult(
            ImmutableArray <IOperation> operationBlocks,
            IMethodSymbol containingMethod,
            AnalyzerOptions analyzerOptions,
            DiagnosticDescriptor rule,
            bool trackInstanceFields,
            bool trackExceptionPaths,
            CancellationToken cancellationToken,
            out DisposeAnalysisResult disposeAnalysisResult,
            out PointsToAnalysisResult pointsToAnalysisResult,
            InterproceduralAnalysisPredicate interproceduralAnalysisPredicateOpt = null,
            bool defaultDisposeOwnershipTransferAtConstructor = false)
        {
            var cfg = operationBlocks.GetControlFlowGraph();

            if (cfg != null)
            {
                disposeAnalysisResult = DisposeAnalysis.GetOrComputeResult(cfg, containingMethod, _wellKnownTypeProvider,
                                                                           analyzerOptions, rule, _disposeOwnershipTransferLikelyTypes, trackInstanceFields,
                                                                           trackExceptionPaths, cancellationToken, out pointsToAnalysisResult,
                                                                           interproceduralAnalysisPredicateOpt: interproceduralAnalysisPredicateOpt,
                                                                           defaultDisposeOwnershipTransferAtConstructor: defaultDisposeOwnershipTransferAtConstructor);
                return(true);
            }

            disposeAnalysisResult  = null;
            pointsToAnalysisResult = null;
            return(false);
        }
Пример #4
0
        private static void ComputeDiagnostics(
            ImmutableDictionary <AbstractLocation, DisposeAbstractValue> disposeData,
            ArrayBuilder <Diagnostic> notDisposedDiagnostics,
            ArrayBuilder <Diagnostic> mayBeNotDisposedDiagnostics,
            DisposeAnalysisResult disposeAnalysisResult,
            PointsToAnalysisResult pointsToAnalysisResult,
            DisposeAnalysisKind disposeAnalysisKind,
            bool isDisposeDataForExceptionPaths)
        {
            foreach (var kvp in disposeData)
            {
                AbstractLocation     location     = kvp.Key;
                DisposeAbstractValue disposeValue = kvp.Value;
                if (disposeValue.Kind == DisposeAbstractValueKind.NotDisposable ||
                    location.Creation == null)
                {
                    continue;
                }

                var isNotDisposed = disposeValue.Kind == DisposeAbstractValueKind.NotDisposed ||
                                    (!disposeValue.DisposingOrEscapingOperations.IsEmpty &&
                                     disposeValue.DisposingOrEscapingOperations.All(d => d.IsInsideCatchRegion(disposeAnalysisResult.ControlFlowGraph) && !location.GetTopOfCreationCallStackOrCreation().IsInsideCatchRegion(disposeAnalysisResult.ControlFlowGraph)));
                var isMayBeNotDisposed = !isNotDisposed && (disposeValue.Kind == DisposeAbstractValueKind.MaybeDisposed || disposeValue.Kind == DisposeAbstractValueKind.NotDisposedOrEscaped);

                if (isNotDisposed ||
                    (isMayBeNotDisposed && disposeAnalysisKind.AreMayBeNotDisposedViolationsEnabled()))
                {
                    var syntax = location.TryGetNodeToReportDiagnostic(pointsToAnalysisResult);
                    if (syntax == null)
                    {
                        continue;
                    }

                    // CA2000: Call System.IDisposable.Dispose on object created by '{0}' before all references to it are out of scope.
                    var rule = GetRule(isNotDisposed);

                    // Ensure that we do not include multiple lines for the object creation expression in the diagnostic message.
                    var argument       = syntax.ToString();
                    var indexOfNewLine = argument.IndexOf(Environment.NewLine, StringComparison.Ordinal);
                    if (indexOfNewLine > 0)
                    {
                        argument = argument.Substring(0, indexOfNewLine);
                    }

                    var diagnostic = syntax.CreateDiagnostic(rule, argument);
                    if (isNotDisposed)
                    {
                        notDisposedDiagnostics.Add(diagnostic);
                    }
                    else
                    {
                        mayBeNotDisposedDiagnostics.Add(diagnostic);
                    }
                }
            }

            DiagnosticDescriptor GetRule(bool isNotDisposed)
            {
                if (isNotDisposed)
                {
                    return(isDisposeDataForExceptionPaths ? NotDisposedOnExceptionPathsRule : NotDisposedRule);
                }
                else
                {
                    return(isDisposeDataForExceptionPaths ? MayBeDisposedOnExceptionPathsRule : MayBeDisposedRule);
                }
            }
        }