File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\SymbolKey\SymbolKey.ParameterSymbolKey.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.Collections.Immutable;
using System.Diagnostics;
 
namespace Microsoft.CodeAnalysis;
 
internal partial struct SymbolKey
{
    private sealed class ParameterSymbolKey : AbstractSymbolKey<IParameterSymbol>
    {
        public static readonly ParameterSymbolKey Instance = new();
 
        public sealed override void Create(IParameterSymbol symbol, SymbolKeyWriter visitor)
        {
            visitor.WriteString(symbol.MetadataName);
            visitor.WriteInteger(symbol.Ordinal);
            visitor.WriteSymbolKey(symbol.ContainingSymbol);
        }
 
        protected sealed override SymbolKeyResolution Resolve(
            SymbolKeyReader reader, IParameterSymbol? contextualSymbol, out string? failureReason)
        {
            var metadataName = reader.ReadRequiredString();
            var ordinal = reader.ReadInteger();
 
            // Parameters are owned by members, and members are never resolved in a way where we have contextual
            // types to guide how the outer parts of the member may resolve.  We can use contextual typing for the
            // *signature* portion of the member though.
            var containingSymbolResolution = reader.ReadSymbolKey(
                contextualSymbol?.ContainingSymbol, out var containingSymbolFailureReason);
 
            if (containingSymbolFailureReason != null)
            {
                failureReason = $"({nameof(ParameterSymbolKey)} {nameof(containingSymbolResolution)} failed -> {containingSymbolFailureReason})";
                return default;
            }
 
            using var result = PooledArrayBuilder<IParameterSymbol>.GetInstance();
            foreach (var container in containingSymbolResolution)
            {
                switch (container)
                {
                    case IMethodSymbol method:
                        Resolve(result, reader, metadataName, ordinal, method.Parameters);
                        break;
                    case IPropertySymbol property:
                        Resolve(result, reader, metadataName, ordinal, property.Parameters);
                        break;
                    case IEventSymbol eventSymbol:
                        // Parameters can be owned by events in VB.  i.e. it's legal in VB to have:
                        //
                        //      Public Event E(a As Integer, b As Integer);
                        //
                        // In this case it's equivalent to:
                        //
                        //      Public Delegate UnutterableCompilerName(a As Integer, b As Integer)
                        //      public Event E As UnutterableCompilerName
                        //
                        // So, in this case, to resolve the parameter, we go have to map the event,
                        // then find the delegate it returns, then find the parameter in the delegate's
                        // 'Invoke' method.
                        var delegateInvoke = (eventSymbol.Type as INamedTypeSymbol)?.DelegateInvokeMethod;
 
                        if (delegateInvoke != null)
                        {
                            Resolve(result, reader, metadataName, ordinal, delegateInvoke.Parameters);
                        }
 
                        break;
                }
            }
 
            return CreateResolution(result, $"({nameof(ParameterSymbolKey)} '{metadataName}' not found)", out failureReason);
        }
 
        private static void Resolve(
            PooledArrayBuilder<IParameterSymbol> result, SymbolKeyReader reader,
            string metadataName, int ordinal, ImmutableArray<IParameterSymbol> parameters)
        {
            // Try to resolve to a parameter with matching name first:
            var hasMatchingName = false;
            foreach (var parameter in parameters)
            {
                if (SymbolKey.Equals(reader.Compilation, parameter.MetadataName, metadataName))
                {
                    result.AddIfNotNull(parameter);
                    hasMatchingName = true;
                }
            }
 
            // The signatures of the containing member matches, so we can fall back to using parameter ordinal.
            // The fallback handles the case when the parameter has been renamed.
            if (!hasMatchingName)
            {
                result.AddIfNotNull(parameters[ordinal]);
            }
        }
    }
}