public bool TryGetOrComputeResult( ImmutableArray <IOperation> operationBlocks, IMethodSymbol containingMethod, bool trackInstanceFields, out DataFlowAnalysisResult <DisposeBlockAnalysisResult, DisposeAbstractValue> disposeAnalysisResult, out DataFlowAnalysisResult <PointsToBlockAnalysisResult, PointsToAbstractValue> pointsToAnalysisResult, out ImmutableDictionary <IFieldSymbol, PointsToAbstractValue> trackedInstanceFieldPointsToMap) { foreach (var operationRoot in operationBlocks) { IBlockOperation topmostBlock = operationRoot.GetTopmostParentBlock(); if (topmostBlock != null) { var cfg = ControlFlowGraph.Create(topmostBlock); // Invoking an instance method may likely invalidate all the instance field analysis state, i.e. // reference type fields might be re-assigned to point to different objects in the called method. // An optimistic points to analysis assumes that the points to values of instance fields don't change on invoking an instance method. // A pessimistic points to analysis resets all the instance state and assumes the instance field might point to any object, hence has unknown state. // For dispose analysis, we want to perform an optimistic points to analysis as we assume a disposable field is not likely to be re-assigned to a separate object in helper method invocations in Dispose. pointsToAnalysisResult = PointsToAnalysis.GetOrComputeResult(cfg, containingMethod, _wellKnownTypeProvider, pessimisticAnalysis: false); disposeAnalysisResult = DisposeAnalysis.GetOrComputeResult(cfg, containingMethod, _wellKnownTypeProvider, _disposeOwnershipTransferLikelyTypes, pointsToAnalysisResult, trackInstanceFields, out trackedInstanceFieldPointsToMap); return(true); } } disposeAnalysisResult = null; pointsToAnalysisResult = null; trackedInstanceFieldPointsToMap = 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); }
public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.RegisterCompilationStartAction(compilationContext => { var iDisposable = WellKnownTypes.IDisposable(compilationContext.Compilation); if (iDisposable == null) { return; } var iCollection = WellKnownTypes.ICollection(compilationContext.Compilation); var genericICollection = WellKnownTypes.GenericICollection(compilationContext.Compilation); var disposeOwnershipTransferLikelyTypes = GetDisposeOwnershipTransferLikelyTypes(compilationContext.Compilation); compilationContext.RegisterOperationBlockStartAction(operationBlockStartContext => { bool hasDisposableCreation = false; operationBlockStartContext.RegisterOperationAction(operationContext => { if (!hasDisposableCreation && operationContext.Operation.Type.IsDisposable(iDisposable)) { hasDisposableCreation = true; } }, OperationKind.ObjectCreation, OperationKind.TypeParameterObjectCreation, OperationKind.DynamicObjectCreation, OperationKind.Invocation); operationBlockStartContext.RegisterOperationBlockEndAction(operationBlockEndContext => { if (!hasDisposableCreation || !(operationBlockEndContext.OwningSymbol is IMethodSymbol containingMethod)) { return; } foreach (var operationRoot in operationBlockEndContext.OperationBlocks) { IBlockOperation topmostBlock = operationRoot.GetTopmostParentBlock(); if (topmostBlock != null) { var cfg = ControlFlowGraph.Create(topmostBlock); var nullAnalysisResult = NullAnalysis.GetOrComputeResult(cfg, containingMethod.ContainingType); var pointsToAnalysisResult = PointsToAnalysis.GetOrComputeResult(cfg, containingMethod.ContainingType, nullAnalysisResult); var disposeAnalysisResult = DisposeAnalysis.GetOrComputeResult(cfg, iDisposable, iCollection, genericICollection, disposeOwnershipTransferLikelyTypes, containingMethod.ContainingType, pointsToAnalysisResult, nullAnalysisResult); ImmutableDictionary <AbstractLocation, DisposeAbstractValue> disposeDataAtExit = disposeAnalysisResult[cfg.Exit].InputData; foreach (var kvp in disposeDataAtExit) { AbstractLocation location = kvp.Key; DisposeAbstractValue disposeValue = kvp.Value; if (disposeValue.Kind == DisposeAbstractValueKind.NotDisposed || ((disposeValue.Kind == DisposeAbstractValueKind.Disposed || disposeValue.Kind == DisposeAbstractValueKind.MaybeDisposed) && disposeValue.DisposingOperations.Count > 0 && disposeValue.DisposingOperations.All(d => d.IsInsideCatchClause()))) { Debug.Assert(location.CreationOpt != null); // 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); operationBlockEndContext.ReportDiagnostic(diagnostic); } } break; } } }); }); }); }