예제 #1
0
        /// <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));
                }
            }
        }
예제 #2
0
        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);
        }
예제 #3
0
        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);
        }
예제 #4
0
        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);
        }