|
// 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]);
}
}
}
}
|