// 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; using System.Linq; using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis; internal partial struct SymbolKey { private sealed class TupleTypeSymbolKey : AbstractSymbolKey<INamedTypeSymbol> { public static readonly TupleTypeSymbolKey Instance = new(); public sealed override void Create(INamedTypeSymbol symbol, SymbolKeyWriter visitor) { Debug.Assert(symbol.IsTupleType); var isError = symbol.TupleUnderlyingType!.TypeKind == TypeKind.Error; using var _1 = ArrayBuilder<string?>.GetInstance(out var friendlyNames); using var _2 = ArrayBuilder<Location?>.GetInstance(out var locations); foreach (var element in symbol.TupleElements) { friendlyNames.Add(element.IsImplicitlyDeclared ? null : element.Name); locations.Add(element.Locations.FirstOrDefault() ?? Location.None); } visitor.WriteBoolean(isError); visitor.WriteStringArray(friendlyNames.ToImmutable()); visitor.WriteLocationArray(locations.ToImmutable()); if (isError) { using var _3 = ArrayBuilder<ISymbol>.GetInstance(out var elementTypes); foreach (var element in symbol.TupleElements) elementTypes.Add(element.Type); visitor.WriteSymbolKeyArray(elementTypes.ToImmutable()); } else { visitor.WriteSymbolKey(symbol.TupleUnderlyingType); } } protected sealed override SymbolKeyResolution Resolve( SymbolKeyReader reader, INamedTypeSymbol? contextualSymbol, out string? failureReason) { contextualSymbol = contextualSymbol is { IsTupleType: true } ? contextualSymbol : null; var isError = reader.ReadBoolean(); return isError ? ResolveErrorTuple(reader, contextualSymbol, out failureReason) : ResolveNormalTuple(reader, contextualSymbol, out failureReason); } private static SymbolKeyResolution ResolveNormalTuple( SymbolKeyReader reader, INamedTypeSymbol? contextualSymbol, out string? failureReason) { using var elementNames = reader.ReadStringArray(); var elementLocations = ReadElementLocations(reader, out var elementLocationsFailureReason); var underlyingTypeResolution = reader.ReadSymbolKey( contextualSymbol?.TupleUnderlyingType, out var underlyingTypeFailureReason); if (underlyingTypeFailureReason != null) { failureReason = $"({nameof(TupleTypeSymbolKey)} {nameof(underlyingTypeResolution)} failed -> {underlyingTypeFailureReason})"; return default; } using var result = PooledArrayBuilder<INamedTypeSymbol>.GetInstance(); var elementNamesArray = elementNames.ToImmutable(); foreach (var namedType in underlyingTypeResolution.OfType<INamedTypeSymbol>()) { // Suppression on elementLocations due to https://github.com/dotnet/roslyn/issues/46527 result.AddIfNotNull(reader.Compilation.CreateTupleTypeSymbol( namedType, elementNamesArray, elementLocations!)); } return CreateResolution(result, $"({nameof(TupleTypeSymbolKey)} failed)", out failureReason); } private static SymbolKeyResolution ResolveErrorTuple( SymbolKeyReader reader, INamedTypeSymbol? contextualType, out string? failureReason) { using var elementNames = reader.ReadStringArray(); var elementLocations = ReadElementLocations(reader, out var elementLocationsFailureReason); using var elementTypes = reader.ReadSymbolKeyArray<INamedTypeSymbol, ITypeSymbol>( contextualType, static (contextualType, i) => SafeGet(contextualType.TupleElements, i)?.Type, out var elementTypesFailureReason); if (elementLocationsFailureReason != null) { failureReason = $"({nameof(TupleTypeSymbolKey)} {nameof(elementLocations)} failed -> {elementLocationsFailureReason})"; return default; } if (elementTypesFailureReason != null) { failureReason = $"({nameof(TupleTypeSymbolKey)} {nameof(elementTypes)} failed -> {elementTypesFailureReason})"; return default; } if (elementTypes.IsDefault) { failureReason = $"({nameof(TupleTypeSymbolKey)} {nameof(elementTypes)} failed)"; return default; } // Suppression on elementLocations due to https://github.com/dotnet/roslyn/issues/46527 var result = reader.Compilation.CreateTupleTypeSymbol( elementTypes.ToImmutable(), elementNames.ToImmutable(), elementLocations!); failureReason = null; return new SymbolKeyResolution(result); } private static ImmutableArray<Location> ReadElementLocations(SymbolKeyReader reader, out string? failureReason) { using var elementLocations = reader.ReadLocationArray(out failureReason); if (failureReason != null) return default; // Compiler API requires that all the locations are non-null, or that there is a default // immutable array passed in. if (elementLocations.Builder.All(loc => loc == null)) return default; return elementLocations.ToImmutable()!; } } } |