File: Rename\ConflictEngine\DeclarationConflictHelpers.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.PooledObjects;
 
namespace Microsoft.CodeAnalysis.Rename.ConflictEngine;
 
internal static class DeclarationConflictHelpers
{
    public static ImmutableArray<Location> GetMembersWithConflictingSignatures(IMethodSymbol renamedMethod, bool trimOptionalParameters)
    {
        var potentiallyConflictingMethods =
            renamedMethod.ContainingType.GetMembers(renamedMethod.Name)
                                        .OfType<IMethodSymbol>()
                                        .Where(m => !m.Equals(renamedMethod) && m.Arity == renamedMethod.Arity);
 
        return GetConflictLocations(renamedMethod, potentiallyConflictingMethods, isMethod: true,
            method => GetAllSignatures(((IMethodSymbol)method).Parameters, trimOptionalParameters));
    }
 
    public static ImmutableArray<Location> GetMembersWithConflictingSignatures(IPropertySymbol renamedProperty, bool trimOptionalParameters)
    {
        var potentiallyConflictingProperties =
            renamedProperty.ContainingType.GetMembers(renamedProperty.Name)
                                        .OfType<IPropertySymbol>()
                                        .Where(m => !m.Equals(renamedProperty) && m.Parameters.Length == renamedProperty.Parameters.Length);
 
        return GetConflictLocations(renamedProperty, potentiallyConflictingProperties, isMethod: false,
            property => GetAllSignatures(((IPropertySymbol)property).Parameters, trimOptionalParameters));
    }
 
    private static ImmutableArray<Location> GetConflictLocations(ISymbol renamedMember,
        IEnumerable<ISymbol> potentiallyConflictingMembers,
        bool isMethod,
        Func<ISymbol, ImmutableArray<ImmutableArray<ITypeSymbol>>> getAllSignatures)
    {
        var signatureToConflictingMember = new Dictionary<ImmutableArray<ITypeSymbol>, ISymbol>(ConflictingSignatureComparer.Instance);
 
        foreach (var member in potentiallyConflictingMembers)
        {
            foreach (var signature in getAllSignatures(member))
            {
                signatureToConflictingMember[signature] = member;
            }
        }
 
        var builder = ArrayBuilder<Location>.GetInstance();
 
        foreach (var signature in getAllSignatures(renamedMember))
        {
            if (signatureToConflictingMember.TryGetValue(signature, out var conflictingSymbol))
            {
                // https://github.com/dotnet/roslyn/issues/73772: add other partial property part as conflicting symbol
                if (isMethod && conflictingSymbol is IMethodSymbol conflictingMethod && renamedMember is IMethodSymbol renamedMethod)
                {
                    if (!(conflictingMethod.PartialDefinitionPart != null && Equals(conflictingMethod.PartialDefinitionPart, renamedMethod)) &&
                        !(conflictingMethod.PartialImplementationPart != null && Equals(conflictingMethod.PartialImplementationPart, renamedMethod)))
                    {
                        builder.AddRange(conflictingSymbol.Locations);
                    }
                }
                else
                {
                    builder.AddRange(conflictingSymbol.Locations);
                }
            }
        }
 
        return builder.ToImmutableAndFree();
    }
 
    private sealed class ConflictingSignatureComparer : IEqualityComparer<ImmutableArray<ITypeSymbol>>
    {
        public static readonly ConflictingSignatureComparer Instance = new();
 
        private ConflictingSignatureComparer() { }
 
        public bool Equals(ImmutableArray<ITypeSymbol> x, ImmutableArray<ITypeSymbol> y)
            => x.SequenceEqual(y);
 
        public int GetHashCode(ImmutableArray<ITypeSymbol> obj)
        {
            // This is a very simple GetHashCode implementation. Doing something "fancier" here
            // isn't really worth it, since to compute a proper hash we'd end up walking all the
            // ITypeSymbols anyways.
            return obj.Length;
        }
    }
 
    private static ImmutableArray<ImmutableArray<ITypeSymbol>> GetAllSignatures(ImmutableArray<IParameterSymbol> parameters, bool trimOptionalParameters)
    {
        var resultBuilder = ArrayBuilder<ImmutableArray<ITypeSymbol>>.GetInstance();
 
        var signatureBuilder = ArrayBuilder<ITypeSymbol>.GetInstance();
 
        foreach (var parameter in parameters)
        {
            // In VB, a method effectively creates multiple signatures which are produced by
            // chopping off each of the optional parameters on the end, last to first, per 4.1.1 of
            // the spec.
            if (trimOptionalParameters && parameter.IsOptional)
            {
                resultBuilder.Add(signatureBuilder.ToImmutable());
            }
 
            signatureBuilder.Add(parameter.Type);
        }
 
        resultBuilder.Add(signatureBuilder.ToImmutableAndFree());
        return resultBuilder.ToImmutableAndFree();
    }
}