private static bool ShouldCallBase(IMethodSymbol method, MethodDeclarationSyntax methodDeclaration, SyntaxNodeAnalysisContext context)
        {
            if (method.Parameters.TrySingle(out var parameter) &&
                parameter.Type == KnownSymbol.Boolean &&
                method.IsOverride &&
                method.OverriddenMethod is IMethodSymbol overridden &&
                !DisposeMethod.TryFindBaseCall(methodDeclaration, context.SemanticModel, context.CancellationToken, out _))
            {
                if (overridden.DeclaringSyntaxReferences.Length == 0)
                {
                    return(true);
                }
                else
                {
                    using (var disposeWalker = DisposeWalker.Borrow(overridden, context.SemanticModel, context.CancellationToken))
                    {
                        foreach (var disposeCall in disposeWalker)
                        {
                            if (DisposeCall.TryGetDisposed(disposeCall, context.SemanticModel, context.CancellationToken, out var disposed) &&
                                FieldOrProperty.TryCreate(disposed, out var fieldOrProperty) &&
                                !DisposableMember.IsDisposed(fieldOrProperty, method, context.SemanticModel, context.CancellationToken))
                            {
                                return(true);
                            }
                        }
                    }
                }
            }

            return(false);
        }
        private static void Handle(SyntaxNodeAnalysisContext context)
        {
            if (!context.IsExcludedFromAnalysis() &&
                context.Node is InvocationExpressionSyntax invocation &&
                DisposeCall.IsIDisposableDispose(invocation, context.SemanticModel, context.CancellationToken) &&
                !invocation.TryFirstAncestorOrSelf <AnonymousFunctionExpressionSyntax>(out _) &&
                DisposeCall.TryGetDisposedRootMember(invocation, context.SemanticModel, context.CancellationToken, out var root))
            {
                if (Disposable.IsCachedOrInjected(root, invocation, context.SemanticModel, context.CancellationToken))
                {
                    context.ReportDiagnostic(Diagnostic.Create(IDISP007DontDisposeInjected.Descriptor, invocation.FirstAncestorOrSelf <StatementSyntax>()?.GetLocation() ?? invocation.GetLocation()));
                }
                else if (context.SemanticModel.TryGetSymbol(root, context.CancellationToken, out ILocalSymbol local))
                {
                    if (IsUsedAfter(local, invocation, context, out var locations))
                    {
                        context.ReportDiagnostic(Diagnostic.Create(IDISP016DontUseDisposedInstance.Descriptor, invocation.FirstAncestorOrSelf <StatementSyntax>()?.GetLocation() ?? invocation.GetLocation(), additionalLocations: locations));
                    }

                    if (IsPreferUsing(local, invocation, context))
                    {
                        context.ReportDiagnostic(Diagnostic.Create(IDISP017PreferUsing.Descriptor, invocation.GetLocation()));
                    }
                }
            }
        }
예제 #3
0
        internal static bool IsDisposedAfter(ISymbol local, ExpressionSyntax location, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            if (location.FirstAncestorOrSelf <MemberDeclarationSyntax>() is MemberDeclarationSyntax scope)
            {
                using (var walker = InvocationWalker.Borrow(scope))
                {
                    foreach (var invocation in walker.Invocations)
                    {
                        if (location.IsExecutedBefore(invocation).IsEither(ExecutedBefore.Maybe, ExecutedBefore.Yes) &&
                            DisposeCall.IsDisposing(invocation, local, semanticModel, cancellationToken))
                        {
                            return(true);
                        }
                    }
                }

                using (var walker = UsingStatementWalker.Borrow(scope))
                {
                    foreach (var usingStatement in walker.UsingStatements)
                    {
                        if (location.IsExecutedBefore(usingStatement) == ExecutedBefore.Yes &&
                            usingStatement.Expression is IdentifierNameSyntax identifierName &&
                            identifierName.Identifier.ValueText == local.Name)
                        {
                            return(true);
                        }
                    }
                }
            }

            return(false);
        }
예제 #4
0
 private static void ReplaceWithUsing(DocumentEditor editor, InvocationExpressionSyntax invocation, CancellationToken cancellationToken)
 {
     if (DisposeCall.TryGetDisposedRootMember(invocation, editor.SemanticModel, cancellationToken, out var root) &&
         editor.SemanticModel.TryGetSymbol(root, cancellationToken, out ILocalSymbol local) &&
         local.TrySingleDeclaration(cancellationToken, out VariableDeclarationSyntax declaration) &&
         invocation.TryFirstAncestor(out ExpressionStatementSyntax expressionStatement) &&
         declaration.Parent is LocalDeclarationStatementSyntax localDeclarationStatement)
     {
         if (expressionStatement.Parent is BlockSyntax finallyBlock &&
             finallyBlock.Parent is FinallyClauseSyntax finallyClause &&
             finallyClause.Parent is TryStatementSyntax tryStatement &&
             !tryStatement.Catches.Any())
         {
             if (declaration.Variables.TrySingle(out var declarator) &&
                 declarator.Initializer?.Value.IsKind(SyntaxKind.NullLiteralExpression) == true &&
                 tryStatement.Block.Statements.TryFirst(out var statement) &&
                 statement is ExpressionStatementSyntax assignExpressionStatement &&
                 assignExpressionStatement.Expression is AssignmentExpressionSyntax assignment)
             {
                 editor.ReplaceNode(
                     tryStatement,
                     SyntaxFactory.UsingStatement(
                         SyntaxFactory.VariableDeclaration(
                             SyntaxFactory.ParseTypeName("var"),
                             SyntaxFactory.SingletonSeparatedList(
                                 declarator.WithInitializer(SyntaxFactory.EqualsValueClause(assignment.Right)))),
                         null,
                         tryStatement.Block.WithStatements(tryStatement.Block.Statements.RemoveAt(0))));
                 editor.RemoveNode(localDeclarationStatement);
             }
예제 #5
0
        internal Result IsMemberDisposed(ISymbol member)
        {
            foreach (var invocation in this.invocations)
            {
                if (DisposeCall.IsDisposing(invocation, member, this.SemanticModel, this.CancellationToken))
                {
                    return(Result.Yes);
                }
            }

            if (member is IPropertySymbol property &&
                property.OverriddenProperty is IPropertySymbol overridden)
            {
                return(this.IsMemberDisposed(overridden));
            }

            foreach (var name in this.identifiers)
            {
                if (member.Name == name.Identifier.ValueText &&
                    this.SemanticModel.TryGetSymbol(name, this.CancellationToken, out ISymbol candidate) &&
                    member.Equals(candidate))
                {
                    return(Result.AssumeYes);
                }
            }

            return(Result.No);
        }
        internal static bool IsDisposed(FieldOrProperty member, MethodDeclarationSyntax disposeMethod, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            using var walker = DisposeWalker.Borrow(disposeMethod, semanticModel, cancellationToken);
            if (Disposable.IsAssignableFrom(member.Type, semanticModel.Compilation))
            {
                foreach (var candidate in walker.Invocations)
                {
                    if (DisposeCall.IsDisposing(candidate, member.Symbol, semanticModel, cancellationToken))
                    {
                        return(true);
                    }
                }
            }

            foreach (var candidate in walker.Identifiers)
            {
                if (candidate.Identifier.Text == member.Name &&
                    semanticModel.TryGetSymbol(candidate, cancellationToken, out var candidateSymbol) &&
                    candidateSymbol.OriginalDefinition.Equals(member.Symbol))
                {
                    return(true);
                }
            }

            return(false);
        }
        private static void Handle(SyntaxNodeAnalysisContext context)
        {
            if (!context.IsExcludedFromAnalysis() &&
                context.Node is InvocationExpressionSyntax invocation &&
                DisposeCall.IsMatchAny(invocation, context.SemanticModel, context.CancellationToken) &&
                !invocation.TryFirstAncestorOrSelf <AnonymousFunctionExpressionSyntax>(out _) &&
                DisposeCall.TryGetDisposedRootMember(invocation, context.SemanticModel, context.CancellationToken, out var root))
            {
                if (Disposable.IsCachedOrInjectedOnly(root, invocation, context.SemanticModel, context.CancellationToken))
                {
                    context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP007DoNotDisposeInjected, invocation.FirstAncestorOrSelf <StatementSyntax>()?.GetLocation() ?? invocation.GetLocation()));
                }

                if (invocation.Expression is MemberAccessExpressionSyntax {
                    Expression : IdentifierNameSyntax _
                } &&
예제 #8
0
        internal static bool IsDisposed(FieldOrProperty member, IMethodSymbol disposeMethod, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            if (disposeMethod == null)
            {
                return(false);
            }

            using (var walker = DisposeWalker.Borrow(disposeMethod, semanticModel, cancellationToken))
            {
                foreach (var invocation in walker)
                {
                    if (DisposeCall.IsDisposing(invocation, member.Symbol, semanticModel, cancellationToken))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
        internal static bool IsDisposedBefore(ISymbol symbol, ExpressionSyntax expression, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            if (TryGetScope(expression, out var block))
            {
                using var walker = InvocationWalker.Borrow(block);
                foreach (var invocation in walker.Invocations)
                {
                    if (invocation.IsExecutedBefore(expression) == ExecutedBefore.No)
                    {
                        continue;
                    }

                    if (DisposeCall.IsDisposing(invocation, symbol, semanticModel, cancellationToken) &&
                        !IsReassignedAfter(block, invocation))
                    {
                        return(true);
                    }
                }
            }

            if (expression is AssignmentExpressionSyntax {
                Left : { } left
            } &&
예제 #10
0
        internal static bool IsDisposedBefore(ISymbol symbol, ExpressionSyntax expression, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            if (TryGetScope(expression, out var block))
            {
                using (var walker = InvocationWalker.Borrow(block))
                {
                    foreach (var invocation in walker.Invocations)
                    {
                        if (invocation.IsExecutedBefore(expression) == ExecutedBefore.No)
                        {
                            continue;
                        }

                        if (DisposeCall.IsDisposing(invocation, symbol, semanticModel, cancellationToken) &&
                            !IsReassignedAfter(block, invocation))
                        {
                            return(true);
                        }
                    }
                }
            }

            if (expression is AssignmentExpressionSyntax assignmentExpression &&
                semanticModel.GetSymbolSafe(assignmentExpression.Left, cancellationToken) is IPropertySymbol property &&
                property.TryGetSetter(cancellationToken, out var setter))
            {
                using (var pooled = InvocationWalker.Borrow(setter))
                {
                    foreach (var invocation in pooled.Invocations)
                    {
                        if ((DisposeCall.IsDisposing(invocation, symbol, semanticModel, cancellationToken) ||
                             DisposeCall.IsDisposing(invocation, property, semanticModel, cancellationToken)) &&
                            !IsReassignedAfter(setter, invocation))
                        {
                            return(true);
                        }
                    }
                }
            }

            return(false);

            bool TryGetScope(SyntaxNode node, out BlockSyntax result)
            {
                result = null;
                if (node.FirstAncestor <AnonymousFunctionExpressionSyntax>() is AnonymousFunctionExpressionSyntax lambda)
                {
                    result = lambda.Body as BlockSyntax;
                }
                else if (node.FirstAncestor <AccessorDeclarationSyntax>() is AccessorDeclarationSyntax accessor)
                {
                    result = accessor.Body;
                }
                else if (node.FirstAncestor <BaseMethodDeclarationSyntax>() is BaseMethodDeclarationSyntax method)
                {
                    result = method.Body;
                }

                return(result != null);
            }

            bool IsReassignedAfter(SyntaxNode scope, InvocationExpressionSyntax disposeCall)
            {
                using (var walker = MutationWalker.Borrow(scope, Scope.Member, semanticModel, cancellationToken))
                {
                    foreach (var mutation in walker.All())
                    {
                        if (mutation.TryFirstAncestor(out StatementSyntax statement) &&
                            disposeCall.IsExecutedBefore(statement) == ExecutedBefore.Yes &&
                            statement.IsExecutedBefore(expression) == ExecutedBefore.Yes)
                        {
                            return(true);
                        }
                    }
                }

                return(false);
            }
        }
예제 #11
0
        internal static bool IsIgnored(ExpressionSyntax node, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            if (node.Parent is AnonymousFunctionExpressionSyntax ||
                node.Parent is UsingStatementSyntax ||
                node.Parent is EqualsValueClauseSyntax ||
                node.Parent is ReturnStatementSyntax ||
                node.Parent is ArrowExpressionClauseSyntax)
            {
                return(false);
            }

            if (node.Parent is StatementSyntax)
            {
                return(true);
            }

            if (node.Parent is AssignmentExpressionSyntax assignment &&
                assignment.Left is IdentifierNameSyntax left &&
                left.Identifier.ValueText == "_")
            {
                return(true);
            }

            if (node.Parent is ArgumentSyntax argument)
            {
                if (argument.Parent is ArgumentListSyntax argumentList &&
                    argumentList.Parent is InvocationExpressionSyntax invocation &&
                    semanticModel.TryGetSymbol(invocation, cancellationToken, out var method) &&
                    method.Name == "Add" &&
                    method.ContainingType.IsAssignableTo(KnownSymbol.IEnumerable, semanticModel.Compilation))
                {
                    if (method.ContainingType == KnownSymbol.CompositeDisposable)
                    {
                        return(false);
                    }

                    if (!method.ContainingType.TypeArguments.Any(x => x.IsAssignableTo(KnownSymbol.IDisposable, semanticModel.Compilation)))
                    {
                        if (MemberPath.TryFindRoot(invocation, out var identifierName) &&
                            semanticModel.TryGetSymbol(identifierName, cancellationToken, out ISymbol symbol) &&
                            FieldOrProperty.TryCreate(symbol, out var fieldOrProperty) &&
                            argument.TryFirstAncestor(out TypeDeclarationSyntax typeDeclaration) &&
                            DisposableMember.IsDisposed(fieldOrProperty, typeDeclaration, semanticModel, cancellationToken) != Result.No)
                        {
                            return(false);
                        }

                        return(true);
                    }

                    return(false);
                }

                return(IsArgumentDisposedByReturnValue(argument, semanticModel, cancellationToken).IsEither(Result.No, Result.AssumeNo) &&
                       IsArgumentAssignedToDisposable(argument, semanticModel, cancellationToken).IsEither(Result.No, Result.AssumeNo));
            }

            if (node.Parent is MemberAccessExpressionSyntax memberAccess)
            {
                if (memberAccess.Parent is InvocationExpressionSyntax invocation &&
                    DisposeCall.IsIDisposableDispose(invocation, semanticModel, cancellationToken))
                {
                    return(false);
                }

                return(IsArgumentDisposedByInvocationReturnValue(memberAccess, semanticModel, cancellationToken).IsEither(Result.No, Result.AssumeNo));
            }

            return(false);
        }
예제 #12
0
        internal static bool IsReturned(ISymbol symbol, SyntaxNode scope, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            using (var walker = ReturnValueWalker.Borrow(scope, ReturnValueSearch.TopLevel, semanticModel, cancellationToken))
            {
                foreach (var value in walker)
                {
                    var candidate = value;
                    switch (candidate)
                    {
                    case CastExpressionSyntax castExpression:
                        candidate = castExpression.Expression;
                        break;

                    case BinaryExpressionSyntax binary when binary.IsKind(SyntaxKind.AsExpression):
                        candidate = binary.Left;

                        break;
                    }

                    if (candidate is ObjectCreationExpressionSyntax objectCreation)
                    {
                        if (objectCreation.ArgumentList != null)
                        {
                            foreach (var argument in objectCreation.ArgumentList.Arguments)
                            {
                                if (semanticModel.TryGetSymbol(argument.Expression, cancellationToken, out ISymbol argumentSymbol) &&
                                    symbol.Equals(argumentSymbol))
                                {
                                    return(true);
                                }
                            }
                        }

                        if (objectCreation.Initializer != null)
                        {
                            foreach (var expression in objectCreation.Initializer.Expressions)
                            {
                                if (semanticModel.TryGetSymbol(expression, cancellationToken, out ISymbol expressionSymbol) &&
                                    symbol.Equals(expressionSymbol))
                                {
                                    return(true);
                                }
                            }
                        }
                    }

                    if (semanticModel.TryGetSymbol(candidate, cancellationToken, out ISymbol returnedSymbol) &&
                        symbol.Equals(returnedSymbol))
                    {
                        return(true);
                    }

                    if (candidate is InvocationExpressionSyntax invocation)
                    {
                        if (returnedSymbol == KnownSymbol.RxDisposable.Create &&
                            invocation.ArgumentList != null &&
                            invocation.ArgumentList.Arguments.TrySingle(out var argument) &&
                            argument.Expression is ParenthesizedLambdaExpressionSyntax lambda)
                        {
                            var body = lambda.Body;
                            using (var pooledInvocations = InvocationWalker.Borrow(body))
                            {
                                foreach (var disposeCandidate in pooledInvocations.Invocations)
                                {
                                    if (DisposeCall.IsDisposing(disposeCandidate, symbol, semanticModel, cancellationToken))
                                    {
                                        return(true);
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return(false);
        }