Exemple #1
0
        private static void BinaryExpressionCheck(EnabledRules rules, SyntaxNode node, SemanticModel semanticModel, bool isAssignmentToReadonly, Action <Diagnostic> reportDiagnostic, string filePath, CancellationToken cancellationToken)
        {
            var binaryExpression = node as BinaryExpressionSyntax;

            bool conversionRuleEnabled = rules.TryGet(AllocationRules.ValueTypeToReferenceTypeConversionRule.Id, out DiagnosticDescriptor conversionRule);

            // as expression
            if (conversionRuleEnabled && binaryExpression.IsKind(SyntaxKind.AsExpression) && binaryExpression.Left != null && binaryExpression.Right != null)
            {
                var leftT  = semanticModel.GetTypeInfo(binaryExpression.Left, cancellationToken);
                var rightT = semanticModel.GetTypeInfo(binaryExpression.Right, cancellationToken);

                if (leftT.Type?.IsValueType == true && rightT.Type?.IsReferenceType == true)
                {
                    reportDiagnostic(Diagnostic.Create(conversionRule, binaryExpression.Left.GetLocation(), EmptyMessageArgs));
                }

                return;
            }

            if (binaryExpression.Right != null)
            {
                if (conversionRuleEnabled)
                {
                    var assignmentExprConversionInfo = semanticModel.GetConversion(binaryExpression.Right, cancellationToken);
                    CheckTypeConversion(conversionRule, assignmentExprConversionInfo, reportDiagnostic, binaryExpression.Right.GetLocation(), filePath);
                }

                var assignmentExprTypeInfo = semanticModel.GetTypeInfo(binaryExpression.Right, cancellationToken);
                CheckDelegateCreation(rules, binaryExpression.Right, assignmentExprTypeInfo, semanticModel, isAssignmentToReadonly, reportDiagnostic, binaryExpression.Right.GetLocation(), filePath, cancellationToken);
                return;
            }
        }
        protected override void AnalyzeNode(SyntaxNodeAnalysisContext context, EnabledRules rules)
        {
            var node          = context.Node;
            var semanticModel = context.SemanticModel;
            Action <Diagnostic> reportDiagnostic = context.ReportDiagnostic;
            var    cancellationToken             = context.CancellationToken;
            string filePath          = node.SyntaxTree.FilePath;
            var    binaryExpressions = node.DescendantNodesAndSelf().OfType <BinaryExpressionSyntax>().Reverse(); // need inner most expressions

            int stringConcatenationCount = 0;

            foreach (var binaryExpression in binaryExpressions)
            {
                if (binaryExpression.Left == null || binaryExpression.Right == null)
                {
                    continue;
                }

                bool isConstant = semanticModel.GetConstantValue(binaryExpression, cancellationToken).HasValue;
                if (isConstant)
                {
                    continue;
                }
                // TODO: TryGetEnabled()
                var left  = semanticModel.GetTypeInfo(binaryExpression.Left, cancellationToken);
                var right = semanticModel.GetTypeInfo(binaryExpression.Right, cancellationToken);

                if (rules.TryGet(AllocationRules.ValueTypeToReferenceTypeInAStringConcatenationRule.Id, out var rule))
                {
                    var leftConversion  = semanticModel.GetConversion(binaryExpression.Left, cancellationToken);
                    var rightConversion = semanticModel.GetConversion(binaryExpression.Right, cancellationToken);
                    CheckTypeConversion(rule, left, leftConversion, reportDiagnostic, binaryExpression.Left.GetLocation(), filePath);
                    CheckTypeConversion(rule, right, rightConversion, reportDiagnostic, binaryExpression.Right.GetLocation(), filePath);
                }

                // regular string allocation
                if (rules.IsEnabled(AllocationRules.StringConcatenationAllocationRule.Id))
                {
                    if (left.Type?.SpecialType == SpecialType.System_String || right.Type?.SpecialType == SpecialType.System_String)
                    {
                        stringConcatenationCount++;
                    }
                }
            }

            if (stringConcatenationCount > 3)
            {
                var rule = rules.Get(AllocationRules.StringConcatenationAllocationRule.Id);
                reportDiagnostic(Diagnostic.Create(rule, node.GetLocation(), EmptyMessageArgs));
            }
        }
Exemple #3
0
        protected override void AnalyzeNode(SyntaxNodeAnalysisContext context, EnabledRules rules)
        {
            var node          = context.Node;
            var semanticModel = context.SemanticModel;
            Action <Diagnostic> reportDiagnostic = context.ReportDiagnostic;
            var    cancellationToken             = context.CancellationToken;
            string filePath = node.SyntaxTree.FilePath;

            // An InitializerExpressionSyntax has an ObjectCreationExpressionSyntax as it's parent, i.e
            // var testing = new TestClass { Name = "Bob" };
            //               |             |--------------| <- InitializerExpressionSyntax or SyntaxKind.ObjectInitializerExpression
            //               |----------------------------| <- ObjectCreationExpressionSyntax or SyntaxKind.ObjectCreationExpression
            if (rules.TryGet(AllocationRules.InitializerCreationRule.Id, out var creationRule))
            {
                var initializerExpression = node as InitializerExpressionSyntax;
                if (initializerExpression?.Parent is ObjectCreationExpressionSyntax)
                {
                    var objectCreation = node.Parent as ObjectCreationExpressionSyntax;
                    var typeInfo       = semanticModel.GetTypeInfo(objectCreation, cancellationToken);
                    if (typeInfo.ConvertedType?.TypeKind != TypeKind.Error &&
                        typeInfo.ConvertedType?.IsReferenceType == true &&
                        objectCreation.Parent?.IsKind(SyntaxKind.EqualsValueClause) == true &&
                        objectCreation.Parent?.Parent?.IsKind(SyntaxKind.VariableDeclarator) == true)
                    {
                        reportDiagnostic(Diagnostic.Create(creationRule, ((VariableDeclaratorSyntax)objectCreation.Parent.Parent).Identifier.GetLocation(), EmptyMessageArgs));
                        return;
                    }
                }
            }

            if (rules.TryGet(AllocationRules.ImplicitArrayCreationRule.Id, out var arrayCreationRule))
            {
                if (node is ImplicitArrayCreationExpressionSyntax implicitArrayExpression)
                {
                    reportDiagnostic(Diagnostic.Create(arrayCreationRule, implicitArrayExpression.NewKeyword.GetLocation(), EmptyMessageArgs));
                    return;
                }
            }

            if (rules.TryGet(AllocationRules.AnonymousNewObjectRule.Id, out var anonCreationRule))
            {
                if (node is AnonymousObjectCreationExpressionSyntax newAnon)
                {
                    reportDiagnostic(Diagnostic.Create(anonCreationRule, newAnon.NewKeyword.GetLocation(), EmptyMessageArgs));
                    return;
                }
            }

            if (rules.TryGet(AllocationRules.NewArrayRule.Id, out var newArrayRule))
            {
                if (node is ArrayCreationExpressionSyntax newArr)
                {
                    reportDiagnostic(Diagnostic.Create(newArrayRule, newArr.NewKeyword.GetLocation(), EmptyMessageArgs));
                    return;
                }
            }

            if (rules.TryGet(AllocationRules.NewObjectRule.Id, out var newObjectRule))
            {
                if (node is ObjectCreationExpressionSyntax newObj)
                {
                    var typeInfo = semanticModel.GetTypeInfo(newObj, cancellationToken);
                    if (typeInfo.ConvertedType != null && typeInfo.ConvertedType.TypeKind != TypeKind.Error && typeInfo.ConvertedType.IsReferenceType)
                    {
                        reportDiagnostic(Diagnostic.Create(newObjectRule, newObj.NewKeyword.GetLocation(), EmptyMessageArgs));
                    }
                    return;
                }
            }

            if (rules.TryGet(AllocationRules.LetCauseRule.Id, out var letClauseRule))
            {
                if (node is LetClauseSyntax letKind)
                {
                    reportDiagnostic(Diagnostic.Create(letClauseRule, letKind.LetKeyword.GetLocation(), EmptyMessageArgs));
                    return;
                }
            }
        }
Exemple #4
0
        protected override void AnalyzeNode(SyntaxNodeAnalysisContext context, EnabledRules rules)
        {
            var node              = context.Node;
            var semanticModel     = context.SemanticModel;
            var cancellationToken = context.CancellationToken;
            Action <Diagnostic> reportDiagnostic = context.ReportDiagnostic;
            string filePath = node.SyntaxTree.FilePath;
            bool   assignedToReadonlyFieldOrProperty =
                (context.ContainingSymbol as IFieldSymbol)?.IsReadOnly == true ||
                (context.ContainingSymbol as IPropertySymbol)?.IsReadOnly == true;

            bool isValueTypeToReferenceRuleEnabled = rules.TryGet(AllocationRules.ValueTypeToReferenceTypeConversionRule.Id,
                                                                  out DiagnosticDescriptor valueTypeToReferenceRule);

            // this.fooObjCall(10);
            // new myobject(10);
            if (node is ArgumentSyntax)
            {
                ArgumentSyntaxCheck(rules, node, semanticModel, assignedToReadonlyFieldOrProperty, reportDiagnostic, filePath, cancellationToken);
            }

            // object foo { get { return 0; } }
            if (isValueTypeToReferenceRuleEnabled && node is ReturnStatementSyntax)
            {
                ReturnStatementExpressionCheck(valueTypeToReferenceRule, node, semanticModel, reportDiagnostic, filePath, cancellationToken);
                return;
            }

            // yield return 0
            if (isValueTypeToReferenceRuleEnabled && node is YieldStatementSyntax)
            {
                YieldReturnStatementExpressionCheck(valueTypeToReferenceRule, node, semanticModel, reportDiagnostic, filePath, cancellationToken);
                return;
            }

            // object a = x ?? 0;
            // var a = 10 as object;
            if (node is BinaryExpressionSyntax)
            {
                BinaryExpressionCheck(rules, node, semanticModel, assignedToReadonlyFieldOrProperty, reportDiagnostic, filePath, cancellationToken);
                return;
            }

            // for (object i = 0;;)
            if (node is EqualsValueClauseSyntax)
            {
                EqualsValueClauseCheck(rules, node, semanticModel, assignedToReadonlyFieldOrProperty, reportDiagnostic, filePath, cancellationToken);
                return;
            }

            // object = true ? 0 : obj
            if (isValueTypeToReferenceRuleEnabled && node is ConditionalExpressionSyntax)
            {
                ConditionalExpressionCheck(valueTypeToReferenceRule, node, semanticModel, reportDiagnostic, filePath, cancellationToken);
                return;
            }

            // string a = $"{1}";
            if (isValueTypeToReferenceRuleEnabled && node is InterpolationSyntax)
            {
                InterpolationCheck(valueTypeToReferenceRule, node, semanticModel, reportDiagnostic, filePath, cancellationToken);
                return;
            }

            // var f = (object)
            if (isValueTypeToReferenceRuleEnabled && node is CastExpressionSyntax)
            {
                CastExpressionCheck(valueTypeToReferenceRule, node, semanticModel, reportDiagnostic, filePath, cancellationToken);
                return;
            }

            // object Foo => 1
            if (node is ArrowExpressionClauseSyntax)
            {
                ArrowExpressionCheck(rules, node, semanticModel, assignedToReadonlyFieldOrProperty, reportDiagnostic, filePath, cancellationToken);
                return;
            }
        }
Exemple #5
0
        private static void CheckDelegateCreation(EnabledRules rules, SyntaxNode node, TypeInfo typeInfo, SemanticModel semanticModel, bool isAssignmentToReadonly, Action <Diagnostic> reportDiagnostic, Location location, string filePath, CancellationToken cancellationToken)
        {
            if (typeInfo.ConvertedType?.TypeKind != TypeKind.Delegate)
            {
                return;
            }

            if (rules.TryGet(AllocationRules.DelegateOnStructInstanceRule.Id, out var delegateOnStructRule))
            {
                var symbolInfo = semanticModel.GetSymbolInfo(node, cancellationToken).Symbol;
                if (symbolInfo?.ContainingType?.IsValueType == true && symbolInfo.GetType().Name == "SourceMemberMethodSymbol")
                {
                    reportDiagnostic(Diagnostic.Create(delegateOnStructRule, location, EmptyMessageArgs));
                }
            }

            // new Action<Foo>(MethodGroup); should skip this one
            var insideObjectCreation = node?.Parent?.Parent?.Parent?.Kind() == SyntaxKind.ObjectCreationExpression;

            if (node is ParenthesizedLambdaExpressionSyntax || node is SimpleLambdaExpressionSyntax ||
                node is AnonymousMethodExpressionSyntax || node is ObjectCreationExpressionSyntax ||
                insideObjectCreation)
            {
                // skip this, because it's intended.
                return;
            }

            if (rules.IsEnabled(AllocationRules.MethodGroupAllocationRule.Id) && node.IsKind(SyntaxKind.IdentifierName))
            {
                if (semanticModel.GetSymbolInfo(node, cancellationToken).Symbol is IMethodSymbol)
                {
                    reportDiagnostic(Diagnostic.Create(rules.Get(AllocationRules.MethodGroupAllocationRule.Id), location, EmptyMessageArgs));
                }
            }
            else if (node.IsKind(SyntaxKind.SimpleMemberAccessExpression))
            {
                var memberAccess = node as MemberAccessExpressionSyntax;
                if (semanticModel.GetSymbolInfo(memberAccess.Name, cancellationToken).Symbol is IMethodSymbol)
                {
                    if (isAssignmentToReadonly && rules.TryGet(AllocationRules.ReadonlyMethodGroupAllocationRule.Id, out var readonlyMethodGroupAllocationRule))
                    {
                        reportDiagnostic(Diagnostic.Create(readonlyMethodGroupAllocationRule, location, EmptyMessageArgs));
                    }
                    else if (rules.TryGet(AllocationRules.MethodGroupAllocationRule.Id, out var methodGroupAllocationRule))
                    {
                        reportDiagnostic(Diagnostic.Create(methodGroupAllocationRule, location, EmptyMessageArgs));
                    }
                }
            }
            else if (node is ArrowExpressionClauseSyntax)
            {
                if (rules.TryGet(AllocationRules.MethodGroupAllocationRule.Id, out var methodGroupAllocationRule))
                {
                    var arrowClause = node as ArrowExpressionClauseSyntax;
                    if (semanticModel.GetSymbolInfo(arrowClause.Expression, cancellationToken).Symbol is IMethodSymbol)
                    {
                        reportDiagnostic(Diagnostic.Create(methodGroupAllocationRule, location, EmptyMessageArgs));
                    }
                }
            }
        }
Exemple #6
0
        protected override void AnalyzeNode(SyntaxNodeAnalysisContext context, EnabledRules rules)
        {
            if (!rules.TryGet(AllocationRules.ReferenceTypeEnumeratorRule.Id, out DiagnosticDescriptor rule))
            {
                return;
            }

            var node          = context.Node;
            var semanticModel = context.SemanticModel;
            Action <Diagnostic> reportDiagnostic = context.ReportDiagnostic;
            var    cancellationToken             = context.CancellationToken;
            string filePath = node.SyntaxTree.FilePath;

            if (node is ForEachStatementSyntax foreachExpression)
            {
                var typeInfo = semanticModel.GetTypeInfo(foreachExpression.Expression, cancellationToken);
                if (typeInfo.Type == null)
                {
                    return;
                }

                if (typeInfo.Type.Name == "String" && typeInfo.Type.ContainingNamespace.Name == "System")
                {
                    // Special case for System.String which is optmizined by
                    // the compiler and does not result in an allocation.
                    return;
                }

                // Regular way of getting the enumerator
                ImmutableArray <ISymbol> enumerator = typeInfo.Type.GetMembers("GetEnumerator");
                if ((enumerator == null || enumerator.Length == 0) && typeInfo.ConvertedType != null)
                {
                    // 1st we try and fallback to using the ConvertedType
                    enumerator = typeInfo.ConvertedType.GetMembers("GetEnumerator");
                }
                if ((enumerator == null || enumerator.Length == 0) && typeInfo.Type.Interfaces != null)
                {
                    // 2nd fallback, now we try and find the IEnumerable Interface explicitly
                    var iEnumerable = typeInfo.Type.Interfaces.Where(i => i.Name == "IEnumerable").ToImmutableArray();
                    if (iEnumerable != null && iEnumerable.Length > 0)
                    {
                        enumerator = iEnumerable[0].GetMembers("GetEnumerator");
                    }
                }

                if (enumerator != null && enumerator.Length > 0)
                {
                    if (enumerator[0] is IMethodSymbol methodSymbol) // probably should do something better here, hack.
                    {
                        if (methodSymbol.ReturnType.IsReferenceType && methodSymbol.ReturnType.SpecialType != SpecialType.System_Collections_IEnumerator)
                        {
                            reportDiagnostic(Diagnostic.Create(rule, foreachExpression.InKeyword.GetLocation(), EmptyMessageArgs));
                        }
                    }
                }

                return;
            }

            if (node is InvocationExpressionSyntax invocationExpression)
            {
                var methodInfo = semanticModel.GetSymbolInfo(invocationExpression, cancellationToken).Symbol as IMethodSymbol;
                if (methodInfo?.ReturnType != null && methodInfo.ReturnType.IsReferenceType)
                {
                    if (methodInfo.ReturnType.AllInterfaces != null)
                    {
                        foreach (var @interface in methodInfo.ReturnType.AllInterfaces)
                        {
                            if (@interface.SpecialType == SpecialType.System_Collections_Generic_IEnumerator_T || @interface.SpecialType == SpecialType.System_Collections_IEnumerator)
                            {
                                reportDiagnostic(Diagnostic.Create(rule, invocationExpression.GetLocation(), EmptyMessageArgs));
                            }
                        }
                    }
                }
            }
        }
Exemple #7
0
        protected override void AnalyzeNode(SyntaxNodeAnalysisContext context, EnabledRules rules)
        {
            var node              = context.Node;
            var semanticModel     = context.SemanticModel;
            var cancellationToken = context.CancellationToken;
            Action <Diagnostic> reportDiagnostic = context.ReportDiagnostic;

            bool genericRuleEnabled = rules.TryGet(AllocationRules.LambaOrAnonymousMethodInGenericMethodRule.Id, out DiagnosticDescriptor genericRule);
            bool driverRuleEnabled  = rules.TryGet(AllocationRules.ClosureDriverRule.Id, out DiagnosticDescriptor driverRule);
            bool captureRuleEnabled = rules.TryGet(AllocationRules.ClosureCaptureRule.Id, out DiagnosticDescriptor captureRule);

            SyntaxNode       genericCheckNode = null;
            Location         location         = null;
            DataFlowAnalysis flowAnalysis     = null;

            var anonExpr = node as AnonymousMethodExpressionSyntax;

            if (anonExpr?.Block?.ChildNodes() != null && anonExpr.Block.ChildNodes().Any())
            {
                if (genericRuleEnabled)
                {
                    genericCheckNode = node;
                    location         = anonExpr.DelegateKeyword.GetLocation();
                }

                if (captureRuleEnabled || driverRuleEnabled)
                {
                    flowAnalysis = semanticModel.AnalyzeDataFlow(anonExpr.Block.ChildNodes().First(), anonExpr.Block.ChildNodes().Last());
                    location     = anonExpr.DelegateKeyword.GetLocation();
                }
            }

            if (node is SimpleLambdaExpressionSyntax lambdaExpr)
            {
                if (genericRuleEnabled)
                {
                    genericCheckNode = node;
                    location         = lambdaExpr.ArrowToken.GetLocation();
                }

                if (captureRuleEnabled || driverRuleEnabled)
                {
                    flowAnalysis = semanticModel.AnalyzeDataFlow(lambdaExpr);
                    location     = lambdaExpr.ArrowToken.GetLocation();
                }
            }

            if (node is ParenthesizedLambdaExpressionSyntax parenLambdaExpr)
            {
                if (genericRuleEnabled)
                {
                    genericCheckNode = node;
                    location         = parenLambdaExpr.ArrowToken.GetLocation();
                }

                if (captureRuleEnabled || driverRuleEnabled)
                {
                    flowAnalysis = semanticModel.AnalyzeDataFlow(parenLambdaExpr);
                    location     = parenLambdaExpr.ArrowToken.GetLocation();
                }
            }

            if (genericCheckNode != null)
            {
                GenericMethodCheck(genericRule, semanticModel, genericCheckNode, location, reportDiagnostic, cancellationToken);
            }

            if (captureRuleEnabled)
            {
                ClosureCaptureDataFlowAnalysis(captureRule, flowAnalysis, reportDiagnostic, location);
            }

            if (driverRuleEnabled)
            {
                if (flowAnalysis?.Captured.Length > 0)
                {
                    reportDiagnostic(Diagnostic.Create(driverRule, location, new[] { string.Join(",", flowAnalysis.Captured.Select(x => x.Name)) }));
                }
            }
        }