Example #1
0
 internal static bool TryGetReturnExpression(this BlockSyntax body, SemanticModel semanticModel, CancellationToken cancellationToken, out ExpressionSyntax returnValue)
 {
     return(ReturnValueWalker.TrygetSingle(body, semanticModel, cancellationToken, out returnValue));
 }
Example #2
0
        private static bool CanIgnore(InvocationExpressionSyntax invocation, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            var method = (IMethodSymbol)semanticModel.GetSymbolSafe(invocation, cancellationToken);

            if (method == null || method.ReturnsVoid)
            {
                return(true);
            }

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

            if (invocation.Parent is ExpressionStatementSyntax &&
                invocation.Parent.Parent is BlockSyntax)
            {
                if (method == KnownSymbol.StringBuilder.Append ||
                    method == KnownSymbol.StringBuilder.AppendLine ||
                    method == KnownSymbol.StringBuilder.AppendFormat ||
                    method == KnownSymbol.IList.Add ||
                    method == KnownSymbol.IList.Remove)
                {
                    return(true);
                }

                if (method.TryGetSingleDeclaration(cancellationToken, out MethodDeclarationSyntax declaration))
                {
                    using (var pooled = ReturnValueWalker.Create(declaration, Search.Recursive, semanticModel, cancellationToken))
                    {
                        if (method.IsExtensionMethod)
                        {
                            var identifier = declaration.ParameterList.Parameters[0].Identifier;
                            foreach (var returnValue in pooled.Item)
                            {
                                if ((returnValue as IdentifierNameSyntax)?.Identifier.ValueText != identifier.ValueText)
                                {
                                    return(false);
                                }
                            }

                            return(true);
                        }

                        foreach (var returnValue in pooled.Item)
                        {
                            if (!returnValue.IsKind(SyntaxKind.ThisExpression))
                            {
                                return(false);
                            }
                        }

                        return(true);
                    }
                }

                return(method.ReturnsVoid);
            }

            return(true);
        }
        private static bool CanIgnore(InvocationExpressionSyntax invocation, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            if (!(invocation.Parent is StatementSyntax))
            {
                return(true);
            }

            if (invocation.Parent is ExpressionStatementSyntax &&
                invocation.Parent.Parent is BlockSyntax &&
                semanticModel.GetSymbolSafe(invocation, cancellationToken) is IMethodSymbol method)
            {
                if (method.ReturnsVoid)
                {
                    return(true);
                }

                if (ReferenceEquals(method.ContainingType, method.ReturnType))
                {
                    if (method.ContainingType == KnownSymbol.StringBuilder)
                    {
                        return(true);
                    }
                }

                if (method == KnownSymbol.IList.Add ||
                    method == KnownSymbol.IList.Remove)
                {
                    return(true);
                }

                if (method.TryGetSingleDeclaration(cancellationToken, out MethodDeclarationSyntax declaration))
                {
                    using (var walker = ReturnValueWalker.Borrow(declaration, Search.Recursive, semanticModel, cancellationToken))
                    {
                        if (method.IsExtensionMethod)
                        {
                            var identifier = declaration.ParameterList.Parameters[0].Identifier;
                            foreach (var returnValue in walker)
                            {
                                if ((returnValue as IdentifierNameSyntax)?.Identifier.ValueText != identifier.ValueText)
                                {
                                    return(false);
                                }
                            }

                            return(true);
                        }

                        foreach (var returnValue in walker)
                        {
                            if (!returnValue.IsKind(SyntaxKind.ThisExpression))
                            {
                                return(false);
                            }
                        }

                        return(true);
                    }
                }

                return(method.ReturnsVoid);
            }

            return(true);
        }
Example #4
0
        private static void Handle(SyntaxNodeAnalysisContext context)
        {
            if (context.IsExcludedFromAnalysis())
            {
                return;
            }

            if (context.Node is PropertyDeclarationSyntax propertyDeclaration &&
                context.ContainingSymbol is IPropertySymbol property)
            {
                {
                    if (propertyDeclaration.ExpressionBody is ArrowExpressionClauseSyntax expressionBody)
                    {
                        if (property.Type.IsReferenceType &&
                            expressionBody.Expression is ObjectCreationExpressionSyntax)
                        {
                            context.ReportDiagnostic(Diagnostic.Create(GU0021CalculatedPropertyAllocates.Descriptor, expressionBody.GetLocation()));
                        }
                        else if (expressionBody.Expression is MemberAccessExpressionSyntax memberAccess &&
                                 IsRelayReturn(memberAccess, context.SemanticModel, context.CancellationToken))
                        {
                            context.ReportDiagnostic(Diagnostic.Create(GU0008AvoidRelayProperties.Descriptor, memberAccess.GetLocation()));
                        }
                    }
                    else if (propertyDeclaration.TryGetGetter(out var getter))
                    {
                        using (var walker = ReturnValueWalker.Borrow(getter, Search.Recursive, context.SemanticModel, context.CancellationToken))
                        {
                            if (walker.TrySingle(out var returnValue))
                            {
                                if (property.Type.IsReferenceType &&
                                    returnValue is ObjectCreationExpressionSyntax)
                                {
                                    if (getter.Contains(returnValue) &&
                                        returnValue.FirstAncestor <ReturnStatementSyntax>() is ReturnStatementSyntax returnStatement)
                                    {
                                        context.ReportDiagnostic(Diagnostic.Create(GU0021CalculatedPropertyAllocates.Descriptor, returnStatement.GetLocation()));
                                    }
                                    else
                                    {
                                        context.ReportDiagnostic(Diagnostic.Create(GU0021CalculatedPropertyAllocates.Descriptor, getter.GetLocation()));
                                    }
                                }
                                else if (returnValue is MemberAccessExpressionSyntax memberAccess &&
                                         IsRelayReturn(memberAccess, context.SemanticModel, context.CancellationToken))
                                {
                                    if (getter.Contains(returnValue) &&
                                        returnValue.FirstAncestor <ReturnStatementSyntax>() is ReturnStatementSyntax returnStatement)
                                    {
                                        context.ReportDiagnostic(Diagnostic.Create(GU0008AvoidRelayProperties.Descriptor, returnStatement.GetLocation()));
                                    }
                                    else
                                    {
                                        context.ReportDiagnostic(Diagnostic.Create(GU0008AvoidRelayProperties.Descriptor, getter.GetLocation()));
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
            private bool AddRecursiveValues(ExpressionSyntax assignedValue)
            {
                if (assignedValue == null ||
                    assignedValue.IsMissing ||
                    !this.checkedLocations.Add(assignedValue))
                {
                    return(false);
                }

                if (assignedValue is LiteralExpressionSyntax ||
                    assignedValue is DefaultExpressionSyntax ||
                    assignedValue is TypeOfExpressionSyntax ||
                    assignedValue is ObjectCreationExpressionSyntax ||
                    assignedValue is ArrayCreationExpressionSyntax ||
                    assignedValue is ImplicitArrayCreationExpressionSyntax ||
                    assignedValue is InitializerExpressionSyntax)
                {
                    this.values.Add(assignedValue);
                    return(true);
                }

                var argument = assignedValue.Parent as ArgumentSyntax;

                if (argument?.RefOrOutKeyword.IsKind(SyntaxKind.OutKeyword) == true)
                {
                    var invocation    = assignedValue.FirstAncestor <InvocationExpressionSyntax>();
                    var invokedMethod = this.semanticModel.GetSymbolSafe(invocation, this.cancellationToken);
                    if (invokedMethod == null ||
                        invokedMethod.DeclaringSyntaxReferences.Length == 0)
                    {
                        this.values.Add(invocation);
                        return(true);
                    }

                    var before = this.values.Count;
                    foreach (var reference in invokedMethod.DeclaringSyntaxReferences)
                    {
                        var methodDeclaration = reference.GetSyntax(this.cancellationToken) as MethodDeclarationSyntax;
                        if (methodDeclaration.TryGetMatchingParameter(argument, out ParameterSyntax parameter))
                        {
                            using (var pooled = AssignedValueWalker.Create(this.semanticModel.GetDeclaredSymbolSafe(parameter, this.cancellationToken), this.semanticModel, this.cancellationToken))
                            {
                                pooled.Item.HandleInvoke(invokedMethod, invocation.ArgumentList);
                                return(this.AddManyRecursively(pooled.Item));
                            }
                        }
                    }

                    return(before != this.values.Count);
                }

                if (assignedValue is BinaryExpressionSyntax binaryExpression)
                {
                    switch (binaryExpression.Kind())
                    {
                    case SyntaxKind.CoalesceExpression:
                        var left  = this.AddRecursiveValues(binaryExpression.Left);
                        var right = this.AddRecursiveValues(binaryExpression.Right);
                        return(left || right);

                    case SyntaxKind.AsExpression:
                        return(this.AddRecursiveValues(binaryExpression.Left));

                    default:
                        return(false);
                    }
                }

                if (assignedValue is CastExpressionSyntax cast)
                {
                    return(this.AddRecursiveValues(cast.Expression));
                }

                if (assignedValue is ConditionalExpressionSyntax conditional)
                {
                    var whenTrue  = this.AddRecursiveValues(conditional.WhenTrue);
                    var whenFalse = this.AddRecursiveValues(conditional.WhenFalse);
                    return(whenTrue || whenFalse);
                }

                if (assignedValue is AwaitExpressionSyntax @await)
                {
                    using (var pooled = ReturnValueWalker.Create(@await, Search.Recursive, this.semanticModel, this.cancellationToken))
                    {
                        return(this.AddManyRecursively(pooled.Item));
                    }
                }

                if (assignedValue is ElementAccessExpressionSyntax)
                {
                    this.values.Add(assignedValue);
                    return(true);
                }

                var symbol = this.semanticModel.GetSymbolSafe(assignedValue, this.cancellationToken);

                if (symbol == null)
                {
                    return(false);
                }

                if (symbol is IFieldSymbol)
                {
                    this.values.Add(assignedValue);
                    return(true);
                }

                if (symbol is IParameterSymbol)
                {
                    this.values.Add(assignedValue);
                    using (var pooled = AssignedValueWalker.Create(assignedValue, this.semanticModel, this.cancellationToken))
                    {
                        return(this.AddManyRecursively(pooled.Item));
                    }
                }

                if (symbol is ILocalSymbol)
                {
                    using (var pooled = AssignedValueWalker.Create(assignedValue, this.semanticModel, this.cancellationToken))
                    {
                        return(this.AddManyRecursively(pooled.Item));
                    }
                }

                if (symbol is IPropertySymbol property)
                {
                    if (property.DeclaringSyntaxReferences.Length == 0)
                    {
                        this.values.Add(assignedValue);
                        return(true);
                    }

                    using (var returnValues = ReturnValueWalker.Create(assignedValue, Search.Recursive, this.semanticModel, this.cancellationToken))
                    {
                        return(this.AddManyRecursively(returnValues.Item));
                    }
                }

                if (symbol is IMethodSymbol method)
                {
                    if (method.DeclaringSyntaxReferences.Length == 0)
                    {
                        this.values.Add(assignedValue);
                        return(true);
                    }

                    using (var pooled = ReturnValueWalker.Create(assignedValue, Search.Recursive, this.semanticModel, this.cancellationToken))
                    {
                        return(this.AddManyRecursively(pooled.Item));
                    }
                }

                return(false);
            }
        private static bool MustBeHandled(
            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 (Disposable.IsCreation(node, semanticModel, cancellationToken)
                .IsEither(Result.No, Result.Unknown))
            {
                return(false);
            }

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

            if (node.Parent is ArgumentSyntax argument)
            {
                if (argument.Parent.Parent is InvocationExpressionSyntax invocation)
                {
                    using (var returnWalker = ReturnValueWalker.Create(invocation, Search.Recursive, semanticModel, cancellationToken))
                    {
                        foreach (var returnValue in returnWalker.Item)
                        {
                            if (MustBeHandled(returnValue, semanticModel, cancellationToken))
                            {
                                return(true);
                            }
                        }
                    }

                    return(false);
                }

                if (argument.Parent.Parent is ObjectCreationExpressionSyntax)
                {
                    if (TryGetAssignedFieldOrProperty(argument, semanticModel, cancellationToken, out ISymbol member, out IMethodSymbol 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 (Disposable.TryGetDisposeMethod(chainedCtor.ContainingType, Search.TopLevel, out IMethodSymbol disposeMethod))
                                {
                                    return(!Disposable.IsMemberDisposed(member, disposeMethod, semanticModel, cancellationToken));
                                }
                            }
                        }

                        if (Disposable.IsMemberDisposed(member, ctor.ContainingType, semanticModel, cancellationToken)
                            .IsEither(Result.Yes, Result.Maybe))
                        {
                            return(false);
                        }

                        return(true);
                    }

                    return(ctor?.ContainingType != KnownSymbol.StreamReader &&
                           ctor?.ContainingType != KnownSymbol.CompositeDisposable);
                }
            }

            return(false);
        }
Example #7
0
        private static bool IsReturned(ILocalSymbol symbol, BlockSyntax block, SemanticModel semanticModel, CancellationToken cancellationToken)
        {
            using (var pooled = ReturnValueWalker.Create(block, Search.TopLevel, semanticModel, cancellationToken))
            {
                foreach (var value in pooled.Item)
                {
                    var returnedSymbol = semanticModel.GetSymbolSafe(value, cancellationToken);
                    if (SymbolComparer.Equals(symbol, returnedSymbol))
                    {
                        return(true);
                    }

                    if (value is ObjectCreationExpressionSyntax objectCreation)
                    {
                        if (objectCreation.ArgumentList != null)
                        {
                            foreach (var argument in objectCreation.ArgumentList.Arguments)
                            {
                                var arg = semanticModel.GetSymbolSafe(argument.Expression, cancellationToken);
                                if (SymbolComparer.Equals(symbol, arg))
                                {
                                    return(true);
                                }
                            }
                        }

                        if (objectCreation.Initializer != null)
                        {
                            foreach (var argument in objectCreation.Initializer.Expressions)
                            {
                                var arg = semanticModel.GetSymbolSafe(argument, cancellationToken);
                                if (SymbolComparer.Equals(symbol, arg))
                                {
                                    return(true);
                                }
                            }
                        }
                    }

                    if (value is InvocationExpressionSyntax invocation)
                    {
                        if (returnedSymbol == KnownSymbol.RxDisposable.Create &&
                            invocation.ArgumentList != null &&
                            invocation.ArgumentList.Arguments.TryGetSingle(out ArgumentSyntax argument))
                        {
                            var body = (argument.Expression as ParenthesizedLambdaExpressionSyntax)?.Body;
                            using (var pooledInvocations = InvocationWalker.Create(body))
                            {
                                foreach (var candidate in pooledInvocations.Item.Invocations)
                                {
                                    if (Disposable.IsDisposing(candidate, symbol, semanticModel, cancellationToken))
                                    {
                                        return(true);
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return(false);
        }