/// <summary> /// Check if any path returns a created IDisposable /// </summary> internal static Result IsAssignedWithCreated(ExpressionSyntax disposable, SemanticModel semanticModel, CancellationToken cancellationToken, out ISymbol assignedSymbol) { if (!IsPotentiallyAssignableTo(disposable, semanticModel, cancellationToken)) { assignedSymbol = null; return(Result.No); } if (semanticModel.GetSymbolSafe(disposable, cancellationToken) is IPropertySymbol property && property.TryGetSetter(cancellationToken, out var setter)) { using (var pooledSet = PooledHashSet <ISymbol> .Borrow()) { using (var pooledAssigned = AssignmentWalker.Borrow(setter, Search.Recursive, semanticModel, cancellationToken)) { foreach (var assigned in pooledAssigned.Assignments) { var symbol = semanticModel.GetSymbolSafe(assigned.Left, cancellationToken); if (IsPotentiallyAssignableTo(assigned.Left, semanticModel, cancellationToken) && (symbol is IFieldSymbol || symbol is IPropertySymbol)) { pooledSet.Add(symbol).IgnoreReturnValue(); } } } assignedSymbol = null; var result = Result.No; foreach (var symbol in pooledSet) { switch (IsAssignedWithCreated(symbol, disposable, semanticModel, cancellationToken)) { case Result.Unknown: if (result == Result.No) { assignedSymbol = symbol; result = Result.Unknown; } break; case Result.Yes: assignedSymbol = symbol; return(Result.Yes); case Result.AssumeYes: assignedSymbol = symbol; result = Result.AssumeYes; break; case Result.No: case Result.AssumeNo: break; default: throw new ArgumentOutOfRangeException(); } } return(result); } } using (var assignedValues = AssignedValueWalker.Borrow(disposable, semanticModel, cancellationToken)) { assignedSymbol = assignedValues.CurrentSymbol; if (assignedValues.Count == 1 && disposable.Parent is AssignmentExpressionSyntax assignment && assignment.Parent is ParenthesizedExpressionSyntax parenthesizedExpression && parenthesizedExpression.Parent is BinaryExpressionSyntax binary && binary.IsKind(SyntaxKind.CoalesceExpression)) { return(Result.No); } using (var recursive = RecursiveValues.Create(assignedValues, semanticModel, cancellationToken)) { return(IsAnyCreation(recursive, semanticModel, cancellationToken)); } } }
internal static Result IsArgumentDisposedByReturnValue(ArgumentSyntax argument, SemanticModel semanticModel, CancellationToken cancellationToken, PooledHashSet <SyntaxNode> visited = null) { if (argument?.Parent is ArgumentListSyntax argumentList) { if (argumentList.Parent is InvocationExpressionSyntax invocation && semanticModel.GetSymbolSafe(invocation, cancellationToken) is IMethodSymbol method) { if (method.ContainingType.DeclaringSyntaxReferences.Length == 0) { if (method == KnownSymbol.CompositeDisposable.Add) { return(Result.Yes); } return(method.ReturnsVoid || !IsAssignableTo(method.ReturnType) ? Result.No : Result.AssumeYes); } if (invocation.TryGetMatchingParameter(argument, semanticModel, cancellationToken, out var parameter)) { return(CheckReturnValues(parameter, invocation, semanticModel, cancellationToken, visited)); } return(Result.Unknown); } if (argumentList.Parent is ObjectCreationExpressionSyntax || argumentList.Parent is ConstructorInitializerSyntax) { if (TryGetAssignedFieldOrProperty(argument, semanticModel, cancellationToken, out var member, out var ctor) && member != null) { var initializer = argument.FirstAncestorOrSelf <ConstructorInitializerSyntax>(); if (initializer != null) { if (semanticModel.GetDeclaredSymbolSafe(initializer.Parent, cancellationToken) is IMethodSymbol chainedCtor && chainedCtor.ContainingType != member.ContainingType) { if (TryGetDelRefMethod(chainedCtor.ContainingType, Search.TopLevel, out var disposeMethod)) { return(IsMemberDisposed(member, disposeMethod, semanticModel, cancellationToken) ? Result.Yes : Result.No); } } } return(IsMemberDisposed(member, ctor.ContainingType, semanticModel, cancellationToken)); } if (ctor == null) { return(Result.AssumeYes); } if (ctor.ContainingType.DeclaringSyntaxReferences.Length == 0) { return(IsAssignableTo(ctor.ContainingType) ? Result.AssumeYes : Result.No); } return(Result.No); } } return(Result.Unknown); }
internal static Result IsArgumentDisposedByInvocationReturnValue(MemberAccessExpressionSyntax memberAccess, SemanticModel semanticModel, CancellationToken cancellationToken, PooledHashSet <SyntaxNode> visited = null) { var symbol = semanticModel.GetSymbolSafe(memberAccess, cancellationToken); if (symbol is IMethodSymbol method) { if (method.ReturnType.Name == "ConfiguredTaskAwaitable") { return(Result.Yes); } if (method.ContainingType.DeclaringSyntaxReferences.Length == 0) { return(method.ReturnsVoid || !IsAssignableTo(method.ReturnType) ? Result.No : Result.AssumeYes); } if (method.IsExtensionMethod && method.ReducedFrom is IMethodSymbol reducedFrom) { var parameter = reducedFrom.Parameters[0]; return(CheckReturnValues(parameter, memberAccess, semanticModel, cancellationToken, visited)); } } if (symbol.IsEither <IFieldSymbol, IPropertySymbol>()) { return(Result.No); } return(Result.Unknown); }
private static Result CheckReturnValues(IParameterSymbol parameter, SyntaxNode memberAccess, SemanticModel semanticModel, CancellationToken cancellationToken, PooledHashSet <SyntaxNode> visited) { Result CheckReturnValue(ExpressionSyntax returnValue) { if (returnValue is ObjectCreationExpressionSyntax nestedObjectCreation) { if (nestedObjectCreation.TryGetMatchingArgument(parameter, out var nestedArgument)) { return(IsArgumentDisposedByReturnValue(nestedArgument, semanticModel, cancellationToken, visited)); } return(Result.No); } if (returnValue is InvocationExpressionSyntax nestedInvocation) { if (nestedInvocation.TryGetMatchingArgument(parameter, out var nestedArgument)) { return(IsArgumentDisposedByReturnValue(nestedArgument, semanticModel, cancellationToken, visited)); } return(Result.No); } if (returnValue is MemberAccessExpressionSyntax nestedMemberAccess) { return(IsArgumentDisposedByInvocationReturnValue(nestedMemberAccess, semanticModel, cancellationToken, visited)); } return(Result.Unknown); } var result = Result.No; using (var returnWalker = ReturnValueWalker.Borrow(memberAccess, Search.Recursive, semanticModel, cancellationToken)) { using (visited = PooledHashSet <SyntaxNode> .BorrowOrIncrementUsage(visited)) { if (!visited.Add(memberAccess)) { return(Result.Unknown); } foreach (var returnValue in returnWalker) { switch (CheckReturnValue(returnValue)) { case Result.Unknown: return(Result.Unknown); case Result.Yes: if (result == Result.No) { result = Result.Yes; } break; case Result.AssumeYes: result = Result.AssumeYes; break; case Result.No: return(Result.No); case Result.AssumeNo: return(Result.AssumeNo); default: throw new ArgumentOutOfRangeException(); } } } } return(result); }