|
// 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.Diagnostics;
namespace Microsoft.CodeAnalysis;
internal partial struct SymbolKey
{
private sealed class PropertySymbolKey : AbstractSymbolKey<IPropertySymbol>
{
public static readonly PropertySymbolKey Instance = new();
public sealed override void Create(IPropertySymbol symbol, SymbolKeyWriter visitor)
{
visitor.WriteString(symbol.MetadataName);
visitor.WriteSymbolKey(symbol.ContainingSymbol);
visitor.WriteBoolean(symbol.IsIndexer);
visitor.WriteBoolean(symbol.PartialDefinitionPart != null);
visitor.WriteRefKindArray(symbol.Parameters);
visitor.WriteParameterTypesArray(symbol.OriginalDefinition.Parameters);
}
protected sealed override SymbolKeyResolution Resolve(
SymbolKeyReader reader, IPropertySymbol? contextualSymbol, out string? failureReason)
{
var metadataName = reader.ReadString();
var containingTypeResolution = reader.ReadSymbolKey(contextualSymbol?.ContainingSymbol, out var containingTypeFailureReason);
var isIndexer = reader.ReadBoolean();
var isPartialImplementationPart = reader.ReadBoolean();
using var refKinds = reader.ReadRefKindArray();
using var properties = GetMembersOfNamedType<IPropertySymbol>(containingTypeResolution, metadataName: null);
using var result = PooledArrayBuilder<IPropertySymbol>.GetInstance();
// For each property that we look at, we'll have to resolve the parameter list and return type in the
// context of that method. This makes sure we can attempt to resolve the parameter list types against
// error types in the property we're currently looking at.
//
// Because of this, we keep track of where we are in the reader. Before resolving every parameter list,
// we'll mark which method we're on and we'll rewind to this point.
var beforeParametersPosition = reader.Position;
IPropertySymbol? property = null;
foreach (var candidate in properties)
{
if (candidate.Parameters.Length != refKinds.Count ||
candidate.MetadataName != metadataName ||
candidate.IsIndexer != isIndexer ||
!ParameterRefKindsMatch(candidate.OriginalDefinition.Parameters, refKinds))
{
continue;
}
property = Resolve(reader, isPartialImplementationPart, candidate);
if (property != null)
break;
// reset ourselves so we can check the return-type/parameters against the next candidate.
reader.Position = beforeParametersPosition;
}
if (reader.Position == beforeParametersPosition)
{
// We didn't find a match. Read through the stream one final time so we're at the correct location
// after this PropertySymbolKey.
_ = reader.ReadSymbolKeyArray<IPropertySymbol, ITypeSymbol>(
contextualSymbol: null, getContextualSymbol: null, failureReason: out _);
}
if (containingTypeFailureReason != null)
{
failureReason = $"({nameof(PropertySymbolKey)} {nameof(containingTypeResolution)} failed -> {containingTypeFailureReason})";
return default;
}
if (property == null)
{
failureReason = $"({nameof(PropertySymbolKey)} '{metadataName}' not found)";
return default;
}
failureReason = null;
return new SymbolKeyResolution(property);
}
private static IPropertySymbol? Resolve(
SymbolKeyReader reader,
bool isPartialImplementationPart,
IPropertySymbol property)
{
if (reader.ParameterTypesMatch(
property,
getContextualType: static (property, i) => SafeGet(property.OriginalDefinition.Parameters, i)?.Type,
property.OriginalDefinition.Parameters))
{
if (isPartialImplementationPart)
property = property.PartialImplementationPart ?? property;
Debug.Assert(property != null);
return property;
}
return null;
}
}
}
|