<commit_before>﻿// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Shared.Utilities
{
    internal partial class SymbolEquivalenceComparer
    {
        private class GetHashCodeVisitor
        {
            private readonly SymbolEquivalenceComparer _symbolEquivalenceComparer;
            private readonly bool _compareMethodTypeParametersByIndex;
            private readonly bool _objectAndDynamicCompareEqually;
            private readonly Func<int, IParameterSymbol, int> _parameterAggregator;
            private readonly Func<int, ISymbol, int> _symbolAggregator;
            private readonly Func<int, INamedTypeSymbol, int> _namedTypeAggregator;

            public GetHashCodeVisitor(
                SymbolEquivalenceComparer symbolEquivalenceComparer,
                bool compareMethodTypeParametersByIndex,
                bool objectAndDynamicCompareEqually)
            {
                _symbolEquivalenceComparer = symbolEquivalenceComparer;
                _compareMethodTypeParametersByIndex = compareMethodTypeParametersByIndex;
                _objectAndDynamicCompareEqually = objectAndDynamicCompareEqually;
                _parameterAggregator = (acc, sym) => Hash.Combine(symbolEquivalenceComparer.ParameterEquivalenceComparer.GetHashCode(sym), acc);
                _symbolAggregator = (acc, sym) => GetHashCode(sym, acc);
                _namedTypeAggregator = CombineNamedTypeHashCode;
            }

            public int GetHashCode(ISymbol x, int currentHash)
            {
                if (x == null)
                {
                    return 0;
                }

                x = UnwrapAlias(x);

                // Special case.  If we're comparing signatures then we want to compare 'object'
                // and 'dynamic' as the same.  However, since they're different types, we don't
                // want to bail out using the above check.

                if (x.Kind == SymbolKind.DynamicType ||
                    (_objectAndDynamicCompareEqually && IsObjectType(x)))
                {
                    return Hash.Combine(typeof(IDynamicTypeSymbol), currentHash);
                }

                return GetHashCodeWorker(x, currentHash);
            }

            private int GetHashCodeWorker(ISymbol x, int currentHash)
            {
                switch (x.Kind)
                {
                    case SymbolKind.ArrayType:
                        return CombineHashCodes((IArrayTypeSymbol)x, currentHash);
                    case SymbolKind.Assembly:
                        return CombineHashCodes((IAssemblySymbol)x, currentHash);
                    case SymbolKind.Event:
                        return CombineHashCodes((IEventSymbol)x, currentHash);
                    case SymbolKind.Field:
                        return CombineHashCodes((IFieldSymbol)x, currentHash);
                    case SymbolKind.Label:
                        return CombineHashCodes((ILabelSymbol)x, currentHash);
                    case SymbolKind.Local:
                        return CombineHashCodes((ILocalSymbol)x, currentHash);
                    case SymbolKind.Method:
                        return CombineHashCodes((IMethodSymbol)x, currentHash);
                    case SymbolKind.NetModule:
                        return CombineHashCodes((IModuleSymbol)x, currentHash);
                    case SymbolKind.NamedType:
                        return CombineHashCodes((INamedTypeSymbol)x, currentHash);
                    case SymbolKind.Namespace:
                        return CombineHashCodes((INamespaceSymbol)x, currentHash);
                    case SymbolKind.Parameter:
                        return CombineHashCodes((IParameterSymbol)x, currentHash);
                    case SymbolKind.PointerType:
                        return CombineHashCodes((IPointerTypeSymbol)x, currentHash);
                    case SymbolKind.Property:
                        return CombineHashCodes((IPropertySymbol)x, currentHash);
                    case SymbolKind.RangeVariable:
                        return CombineHashCodes((IRangeVariableSymbol)x, currentHash);
                    case SymbolKind.TypeParameter:
                        return CombineHashCodes((ITypeParameterSymbol)x, currentHash);
                    case SymbolKind.Preprocessing:
                        return CombineHashCodes((IPreprocessingSymbol)x, currentHash);
                    default:
                        return -1;
                }
            }

            private int CombineHashCodes(IArrayTypeSymbol x, int currentHash)
            {
                return
                    Hash.Combine(x.Rank,
                    GetHashCode(x.ElementType, currentHash));
            }

            private int CombineHashCodes(IAssemblySymbol x, int currentHash)
            {
                return Hash.Combine(_symbolEquivalenceComparer._assemblyComparerOpt?.GetHashCode(x) ?? 0, currentHash);
            }

            private int CombineHashCodes(IFieldSymbol x, int currentHash)
            {
                return
                    Hash.Combine(x.Name,
                    GetHashCode(x.ContainingSymbol, currentHash));
            }

            private int CombineHashCodes(ILabelSymbol x, int currentHash)
            {
                return
                    Hash.Combine(x.Name,
                    Hash.Combine(x.Locations.FirstOrDefault(), currentHash));
            }

            private int CombineHashCodes(ILocalSymbol x, int currentHash)
            {
                return Hash.Combine(x.Locations.FirstOrDefault(), currentHash);
            }

            private static int CombineHashCodes<T>(ImmutableArray<T> array, int currentHash, Func<int, T, int> func)
            {
                return array.Aggregate<int, T>(currentHash, func);
            }

            private int CombineHashCodes(IMethodSymbol x, int currentHash)
            {
                currentHash = Hash.Combine(x.MetadataName, currentHash);
                if (x.MethodKind == MethodKind.AnonymousFunction)
                {
                    return Hash.Combine(x.Locations.FirstOrDefault(), currentHash);
                }

                currentHash =
                    Hash.Combine(IsPartialMethodImplementationPart(x),
                    Hash.Combine(IsPartialMethodDefinitionPart(x),
                    Hash.Combine(x.IsDefinition,
                    Hash.Combine(IsConstructedFromSelf(x),
                    Hash.Combine(x.Arity,
                    Hash.Combine(x.Parameters.Length,
                    Hash.Combine(x.Name, currentHash)))))));

                var checkContainingType = CheckContainingType(x);
                if (checkContainingType)
                {
                    currentHash = GetHashCode(x.ContainingSymbol, currentHash);
                }

                currentHash =
                    CombineHashCodes(x.Parameters, currentHash, _parameterAggregator);

                return IsConstructedFromSelf(x)
                    ? currentHash
                    : CombineHashCodes(x.TypeArguments, currentHash, _symbolAggregator);
            }

            private int CombineHashCodes(IModuleSymbol x, int currentHash)
            {
                return CombineHashCodes(x.ContainingAssembly, Hash.Combine(x.Name, currentHash));
            }

            private int CombineHashCodes(INamedTypeSymbol x, int currentHash)
            {
                return Unwrap(x).Aggregate(currentHash, _namedTypeAggregator);
            }

            private int CombineNamedTypeHashCode(int currentHash, INamedTypeSymbol x)
            {
                // If we want object and dynamic to be the same, and this is 'object', then return
                // the same hash we do for 'dynamic'.
                currentHash =
                    Hash.Combine(x.IsDefinition,
                    Hash.Combine(IsConstructedFromSelf(x),
                    Hash.Combine(x.Arity,
                    Hash.Combine((int)GetTypeKind(x),
                    Hash.Combine(x.Name,
                    Hash.Combine(x.IsAnonymousType,
                    Hash.Combine(x.IsUnboundGenericType,
                    GetHashCode(x.ContainingSymbol, currentHash))))))));

                if (x.IsAnonymousType)
                {
                    return CombineAnonymousTypeHashCode(x, currentHash);
                }<commit_msg>Don't allocate a iterator each time we compute hashcodes.<commit_after>﻿// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Shared.Utilities
{
    internal partial class SymbolEquivalenceComparer
    {
        private class GetHashCodeVisitor
        {
            private readonly SymbolEquivalenceComparer _symbolEquivalenceComparer;
            private readonly bool _compareMethodTypeParametersByIndex;
            private readonly bool _objectAndDynamicCompareEqually;
            private readonly Func<int, IParameterSymbol, int> _parameterAggregator;
            private readonly Func<int, ISymbol, int> _symbolAggregator;

            public GetHashCodeVisitor(
                SymbolEquivalenceComparer symbolEquivalenceComparer,
                bool compareMethodTypeParametersByIndex,
                bool objectAndDynamicCompareEqually)
            {
                _symbolEquivalenceComparer = symbolEquivalenceComparer;
                _compareMethodTypeParametersByIndex = compareMethodTypeParametersByIndex;
                _objectAndDynamicCompareEqually = objectAndDynamicCompareEqually;
                _parameterAggregator = (acc, sym) => Hash.Combine(symbolEquivalenceComparer.ParameterEquivalenceComparer.GetHashCode(sym), acc);
                _symbolAggregator = (acc, sym) => GetHashCode(sym, acc);
            }

            public int GetHashCode(ISymbol x, int currentHash)
            {
                if (x == null)
                {
                    return 0;
                }

                x = UnwrapAlias(x);

                // Special case.  If we're comparing signatures then we want to compare 'object'
                // and 'dynamic' as the same.  However, since they're different types, we don't
                // want to bail out using the above check.

                if (x.Kind == SymbolKind.DynamicType ||
                    (_objectAndDynamicCompareEqually && IsObjectType(x)))
                {
                    return Hash.Combine(typeof(IDynamicTypeSymbol), currentHash);
                }

                return GetHashCodeWorker(x, currentHash);
            }

            private int GetHashCodeWorker(ISymbol x, int currentHash)
            {
                switch (x.Kind)
                {
                    case SymbolKind.ArrayType:
                        return CombineHashCodes((IArrayTypeSymbol)x, currentHash);
                    case SymbolKind.Assembly:
                        return CombineHashCodes((IAssemblySymbol)x, currentHash);
                    case SymbolKind.Event:
                        return CombineHashCodes((IEventSymbol)x, currentHash);
                    case SymbolKind.Field:
                        return CombineHashCodes((IFieldSymbol)x, currentHash);
                    case SymbolKind.Label:
                        return CombineHashCodes((ILabelSymbol)x, currentHash);
                    case SymbolKind.Local:
                        return CombineHashCodes((ILocalSymbol)x, currentHash);
                    case SymbolKind.Method:
                        return CombineHashCodes((IMethodSymbol)x, currentHash);
                    case SymbolKind.NetModule:
                        return CombineHashCodes((IModuleSymbol)x, currentHash);
                    case SymbolKind.NamedType:
                        return CombineHashCodes((INamedTypeSymbol)x, currentHash);
                    case SymbolKind.Namespace:
                        return CombineHashCodes((INamespaceSymbol)x, currentHash);
                    case SymbolKind.Parameter:
                        return CombineHashCodes((IParameterSymbol)x, currentHash);
                    case SymbolKind.PointerType:
                        return CombineHashCodes((IPointerTypeSymbol)x, currentHash);
                    case SymbolKind.Property:
                        return CombineHashCodes((IPropertySymbol)x, currentHash);
                    case SymbolKind.RangeVariable:
                        return CombineHashCodes((IRangeVariableSymbol)x, currentHash);
                    case SymbolKind.TypeParameter:
                        return CombineHashCodes((ITypeParameterSymbol)x, currentHash);
                    case SymbolKind.Preprocessing:
                        return CombineHashCodes((IPreprocessingSymbol)x, currentHash);
                    default:
                        return -1;
                }
            }

            private int CombineHashCodes(IArrayTypeSymbol x, int currentHash)
            {
                return
                    Hash.Combine(x.Rank,
                    GetHashCode(x.ElementType, currentHash));
            }

            private int CombineHashCodes(IAssemblySymbol x, int currentHash)
            {
                return Hash.Combine(_symbolEquivalenceComparer._assemblyComparerOpt?.GetHashCode(x) ?? 0, currentHash);
            }

            private int CombineHashCodes(IFieldSymbol x, int currentHash)
            {
                return
                    Hash.Combine(x.Name,
                    GetHashCode(x.ContainingSymbol, currentHash));
            }

            private int CombineHashCodes(ILabelSymbol x, int currentHash)
            {
                return
                    Hash.Combine(x.Name,
                    Hash.Combine(x.Locations.FirstOrDefault(), currentHash));
            }

            private int CombineHashCodes(ILocalSymbol x, int currentHash)
            {
                return Hash.Combine(x.Locations.FirstOrDefault(), currentHash);
            }

            private static int CombineHashCodes<T>(ImmutableArray<T> array, int currentHash, Func<int, T, int> func)
            {
                return array.Aggregate<int, T>(currentHash, func);
            }

            private int CombineHashCodes(IMethodSymbol x, int currentHash)
            {
                currentHash = Hash.Combine(x.MetadataName, currentHash);
                if (x.MethodKind == MethodKind.AnonymousFunction)
                {
                    return Hash.Combine(x.Locations.FirstOrDefault(), currentHash);
                }

                currentHash =
                    Hash.Combine(IsPartialMethodImplementationPart(x),
                    Hash.Combine(IsPartialMethodDefinitionPart(x),
                    Hash.Combine(x.IsDefinition,
                    Hash.Combine(IsConstructedFromSelf(x),
                    Hash.Combine(x.Arity,
                    Hash.Combine(x.Parameters.Length,
                    Hash.Combine(x.Name, currentHash)))))));

                var checkContainingType = CheckContainingType(x);
                if (checkContainingType)
                {
                    currentHash = GetHashCode(x.ContainingSymbol, currentHash);
                }

                currentHash =
                    CombineHashCodes(x.Parameters, currentHash, _parameterAggregator);

                return IsConstructedFromSelf(x)
                    ? currentHash
                    : CombineHashCodes(x.TypeArguments, currentHash, _symbolAggregator);
            }

            private int CombineHashCodes(IModuleSymbol x, int currentHash)
            {
                return CombineHashCodes(x.ContainingAssembly, Hash.Combine(x.Name, currentHash));
            }

            private int CombineHashCodes(INamedTypeSymbol x, int currentHash)
            {
                currentHash = CombineNamedTypeHashCode(x, currentHash);

                var errorType = x as IErrorTypeSymbol;
                if (errorType != null)
                {
                    foreach (var candidate in errorType.CandidateSymbols)
                    {
                        var candidateNamedType = candidate as INamedTypeSymbol;
                        if (candidateNamedType != null)
                        {
                            currentHash = CombineNamedTypeHashCode(candidateNamedType, currentHash);
                        }
                    }
                }

                return currentHash;
            }

            private int CombineNamedTypeHashCode(INamedTypeSymbol x, int currentHash)
            {
                // If we want object and dynamic to be the same, and this is 'object', then return
                // the same hash we do for 'dynamic'.
                currentHash =
                    Hash.Combine(x.IsDefinition,
                    Hash.Combine(IsConstructedFromSelf(x),
                    Hash.Combine(x.Arity,
                    Hash.Combine((int)GetTypeKind(x),
                    Hash.Combine(x.Name,
                    Hash.Combine(x.IsAnonymousType,
                    Hash.Combine(x.IsUnboundGenericType,
                    GetHashCode(x.ContainingSymbol, currentHash))))))));

                if (x.IsAnonymousType)
                {
                    return CombineAnonymousTypeHashCode(x, currentHash);
                }
{'fraction_non_alphanumeric': 0.06727486427492024, 'fraction_numerical': 0.0005596910505401019, 'mean_word_length': 1.604285089637079, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 20, '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': '7492532', 'n_tokens_mistral': 4288, 'n_tokens_neox': 3989, 'n_words': 854}