<commit_before>﻿namespace ClrHeapAllocationAnalyzer
{
    using System;
    using System.Collections.Immutable;
    using System.Threading;
    using Microsoft.CodeAnalysis;
    using Microsoft.CodeAnalysis.CSharp;
    using Microsoft.CodeAnalysis.CSharp.Syntax;
    using Microsoft.CodeAnalysis.Diagnostics;
    using System.Collections;

    [DiagnosticAnalyzer(LanguageNames.CSharp)]
    public sealed class EnumeratorAllocationAnalyzer : ISyntaxNodeAnalyzer<SyntaxKind>
    {
        internal static DiagnosticDescriptor ReferenceTypeEnumeratorRule = new DiagnosticDescriptor("HeapAnalyzerEnumeratorAllocationRule", "Possible allocation of reference type enumerator", "Non-ValueType enumerator may result in an heap allocation", "Performance", DiagnosticSeverity.Warning, true);

        internal static object[] EmptyMessageArgs = { };

        public ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
        {
            get
            {
                return ImmutableArray.Create(ReferenceTypeEnumeratorRule);
            }
        }

        public ImmutableArray<SyntaxKind> SyntaxKindsOfInterest
        {
            get
            {
                return ImmutableArray.Create(SyntaxKind.ForEachStatement, SyntaxKind.InvocationExpression);
            }
        }

        public void AnalyzeNode(SyntaxNode node, SemanticModel semanticModel, Action<Diagnostic> addDiagnostic, AnalyzerOptions options, CancellationToken cancellationToken)
        {
            string filePath = node.SyntaxTree.FilePath;

            var foreachExpression = node as ForEachStatementSyntax;
            if (foreachExpression != null)
            {
                var typeInfo = semanticModel.GetTypeInfo(foreachExpression.Expression);
                if (typeInfo.Type != null)
                {
                    var enumerator = typeInfo.Type.GetMembers("GetEnumerator");
                    if (enumerator == null || enumerator.Length == 0)
                    {
                        // Fallback to the ConvertedType
                        enumerator = typeInfo.ConvertedType.GetMembers("GetEnumerator");
                    }
                    if (enumerator == null || enumerator.Length == 0)
                    {
                        // 2nd Fallback, now find the IEnumerable Interface explicitly
                        var iEnumerable = typeInfo.Type.Interfaces.WhereAsArray(i => i.Name == "IEnumerable");
                        if (iEnumerable != null && iEnumerable.Length > 0)
                        {
                            enumerator = iEnumerable[0].GetMembers("GetEnumerator");
                        }
                    }
                    if (enumerator != null && enumerator.Length > 0)
                    {
                        var methodSymbol = enumerator[0] as IMethodSymbol; // probably should do something better here, hack.
                        if (methodSymbol != null)
                        {
                            if (methodSymbol.ReturnType.IsReferenceType &&
                                methodSymbol.ReturnType.SpecialType != SpecialType.System_Collections_IEnumerator)
                            {
                                addDiagnostic(Diagnostic.Create(ReferenceTypeEnumeratorRule, foreachExpression.InKeyword.GetLocation(), EmptyMessageArgs));
                                HeapAllocationAnalyzerEventSource.Logger.EnumeratorAllocation(filePath);
                            }
                        }<commit_msg>Include more null checks<commit_after>﻿namespace ClrHeapAllocationAnalyzer
{
    using System;
    using System.Collections.Immutable;
    using System.Threading;
    using Microsoft.CodeAnalysis;
    using Microsoft.CodeAnalysis.CSharp;
    using Microsoft.CodeAnalysis.CSharp.Syntax;
    using Microsoft.CodeAnalysis.Diagnostics;
    using System.Collections;

    [DiagnosticAnalyzer(LanguageNames.CSharp)]
    public sealed class EnumeratorAllocationAnalyzer : ISyntaxNodeAnalyzer<SyntaxKind>
    {
        internal static DiagnosticDescriptor ReferenceTypeEnumeratorRule = new DiagnosticDescriptor("HeapAnalyzerEnumeratorAllocationRule", "Possible allocation of reference type enumerator", "Non-ValueType enumerator may result in an heap allocation", "Performance", DiagnosticSeverity.Warning, true);

        internal static object[] EmptyMessageArgs = { };

        public ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
        {
            get
            {
                return ImmutableArray.Create(ReferenceTypeEnumeratorRule);
            }
        }

        public ImmutableArray<SyntaxKind> SyntaxKindsOfInterest
        {
            get
            {
                return ImmutableArray.Create(SyntaxKind.ForEachStatement, SyntaxKind.InvocationExpression);
            }
        }

        public void AnalyzeNode(SyntaxNode node, SemanticModel semanticModel, Action<Diagnostic> addDiagnostic, AnalyzerOptions options, CancellationToken cancellationToken)
        {
            string filePath = node.SyntaxTree.FilePath;

            var foreachExpression = node as ForEachStatementSyntax;
            if (foreachExpression != null)
            {
                var typeInfo = semanticModel.GetTypeInfo(foreachExpression.Expression);
                if (typeInfo.Type != null)
                {
                    // Regular way of getting the enumerator
                    var 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.WhereAsArray(i => i.Name == "IEnumerable");
                        if (iEnumerable != null && iEnumerable.Length > 0)
                        {
                            enumerator = iEnumerable[0].GetMembers("GetEnumerator");
                        }
                    }
                    if (enumerator != null && enumerator.Length > 0)
                    {
                        var methodSymbol = enumerator[0] as IMethodSymbol; // probably should do something better here, hack.
                        if (methodSymbol != null)
                        {
                            if (methodSymbol.ReturnType.IsReferenceType &&
                                methodSymbol.ReturnType.SpecialType != SpecialType.System_Collections_IEnumerator)
                            {
                                addDiagnostic(Diagnostic.Create(ReferenceTypeEnumeratorRule, foreachExpression.InKeyword.GetLocation(), EmptyMessageArgs));
                                HeapAllocationAnalyzerEventSource.Logger.EnumeratorAllocation(filePath);
                            }
                        }
{'fraction_non_alphanumeric': 0.06984171063593446, 'fraction_numerical': 0.0020827547903360176, 'mean_word_length': 1.813671875, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5085046', 'n_tokens_mistral': 1650, 'n_tokens_neox': 1576, 'n_words': 341}