protected override void SetValueForParameterPointsToLocationOnEntry(IParameterSymbol parameter, PointsToAbstractValue pointsToAbstractValue) { if (DisposeOwnershipTransferLikelyTypes.Contains(parameter.Type) || (DisposeOwnershipTransferAtConstructor && parameter.ContainingSymbol.IsConstructor())) { SetAbstractValue(pointsToAbstractValue, DisposeAbstractValue.NotDisposed); } }
protected override void PostProcessArgument(IArgumentOperation operation, bool isEscaped) { base.PostProcessArgument(operation, isEscaped); if (isEscaped) { PostProcessEscapedArgument(); } return; // Local functions. void PostProcessEscapedArgument() { // Discover if a disposable object is being passed into the creation method for this new disposable object // and if the new disposable object assumes ownership of that passed in disposable object. if (IsDisposeOwnershipTransfer()) { var pointsToValue = GetPointsToAbstractValue(operation.Value); HandlePossibleEscapingOperation(operation, pointsToValue.Locations); } } bool IsDisposeOwnershipTransfer() { if (!operation.Parameter.Type.IsDisposable(WellKnownTypeProvider.IDisposable)) { return(false); } switch (operation.Parent) { case IObjectCreationOperation _: return(DisposeOwnershipTransferAtConstructor || DisposeOwnershipTransferLikelyTypes.Contains(operation.Parameter.Type)); case IInvocationOperation invocation: return(IsDisposableCreationSpecialCase(invocation.TargetMethod) && DisposeOwnershipTransferLikelyTypes.Contains(operation.Parameter.Type)); default: return(false); } } }
protected override void PostProcessArgument(IArgumentOperation operation, bool isEscaped) { base.PostProcessArgument(operation, isEscaped); if (isEscaped) { // Discover if a disposable object is being passed into the creation method for this new disposable object // and if the new disposable object assumes ownership of that passed in disposable object. if (IsDisposeOwnershipTransfer()) { var pointsToValue = GetPointsToAbstractValue(operation.Value); HandlePossibleEscapingOperation(operation, pointsToValue.Locations); return; } else if (FlowBranchConditionKind == ControlFlowConditionKind.WhenFalse && operation.Parameter.RefKind == RefKind.Out && operation.Parent is IInvocationOperation invocation && invocation.TargetMethod.ReturnType.SpecialType == SpecialType.System_Boolean) { // Case 1: // // Assume 'obj' is not a valid object on the 'else' path. // if (TryXXX(out IDisposable obj)) // { // obj.Dispose(); // } // // return; // Case 2: // if (!TryXXX(out IDisposable obj)) // { // return; // Assume 'obj' is not a valid object on this path. // } // // obj.Dispose(); HandlePossibleInvalidatingOperation(operation); return; } } // Ref or out argument values from callee might be escaped by assigning to field. if (operation.Parameter.RefKind == RefKind.Out || operation.Parameter.RefKind == RefKind.Ref) { HandlePossibleEscapingOperation(operation, GetEscapedLocations(operation)); } return; // Local functions. bool IsDisposeOwnershipTransfer() { if (operation.Parameter.RefKind == RefKind.Out) { // Out arguments are always owned by the caller. return(false); } return(operation.Parent switch { IObjectCreationOperation _ => DisposeOwnershipTransferAtConstructor || DisposeOwnershipTransferLikelyTypes.Contains(operation.Parameter.Type), IInvocationOperation invocation => DisposeOwnershipTransferAtMethodCall || IsDisposableCreationSpecialCase(invocation.TargetMethod) && DisposeOwnershipTransferLikelyTypes.Contains(operation.Parameter.Type), _ => false, }); }
protected override void PostProcessArgument(IArgumentOperation operation, bool isEscaped) { base.PostProcessArgument(operation, isEscaped); if (isEscaped) { // Discover if a disposable object is being passed into the creation method for this new disposable object // and if the new disposable object assumes ownership of that passed in disposable object. if (IsDisposeOwnershipTransfer()) { var pointsToValue = GetPointsToAbstractValue(operation.Value); HandlePossibleEscapingOperation(operation, pointsToValue.Locations); } else if (FlowBranchConditionKind == ControlFlowConditionKind.WhenFalse && operation.Parameter.RefKind == RefKind.Out && operation.Parent is IInvocationOperation invocation && invocation.TargetMethod.ReturnType.SpecialType == SpecialType.System_Boolean) { // Case 1: // // Assume 'obj' is not a valid object on the 'else' path. // if (TryXXX(out IDisposable obj)) // { // obj.Dispose(); // } // // return; // Case 2: // if (!TryXXX(out IDisposable obj)) // { // return; // Assume 'obj' is not a valid object on this path. // } // // obj.Dispose(); HandlePossibleInvalidatingOperation(operation); } } else if (operation.Parameter.RefKind == RefKind.Out || operation.Parameter.RefKind == RefKind.Ref) { HandlePossibleEscapingOperation(operation, GetEscapedLocations(operation)); } return; // Local functions. bool IsDisposeOwnershipTransfer() { if (!operation.Parameter.Type.IsDisposable(WellKnownTypeProvider.IDisposable)) { return(false); } switch (operation.Parent) { case IObjectCreationOperation _: return(DisposeOwnershipTransferAtConstructor || DisposeOwnershipTransferLikelyTypes.Contains(operation.Parameter.Type)); case IInvocationOperation invocation: return(IsDisposableCreationSpecialCase(invocation.TargetMethod) && DisposeOwnershipTransferLikelyTypes.Contains(operation.Parameter.Type)); default: return(false); } } }