// 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; 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; #if !ROSLYN_4_12_OR_LOWER case INamedTypeSymbol { IsExtension: true, ExtensionParameter: { } extensionParameter }: Resolve(result, reader, metadataName, ordinal, [extensionParameter]); break; #endif } } 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]); } } } } |