File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\SymbolKey\SymbolKey.AnonymousFunctionOrDelegateSymbolKey.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.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.Shared.Extensions;
 
namespace Microsoft.CodeAnalysis;
 
internal partial struct SymbolKey
{
    /// <summary>
    /// Anonymous functions and anonymous-delegates (the special VB synthesized delegate types),
    /// only come into existence when someone has explicitly written a lambda in their source 
    /// code. So to appropriately round-trip this symbol we store the location that the lambda
    /// was at so that we can find the symbol again when we resolve the key.
    /// </summary>
    private static class AnonymousFunctionOrDelegateSymbolKey
    {
        public static void Create(ISymbol symbol, SymbolKeyWriter visitor)
        {
            Debug.Assert(symbol.IsAnonymousDelegateType() || symbol.IsAnonymousFunction());
 
            // Write out if this was an anonymous delegate or anonymous function.
            // In both cases they'll have the same location (the location of 
            // the lambda that forced them into existence).  When we resolve the
            // symbol later, if it's an anonymous delegate, we'll first resolve to
            // the anonymous-function, then use that anonymous-functoin to get at
            // the synthesized anonymous delegate.
            visitor.WriteBoolean(symbol.IsAnonymousDelegateType());
            visitor.WriteLocation(symbol.Locations.First());
        }
 
        public static SymbolKeyResolution Resolve(SymbolKeyReader reader, out string? failureReason)
        {
            var isAnonymousDelegateType = reader.ReadBoolean();
            var location = reader.ReadLocation(out var locationFailureReason)!;
 
            if (locationFailureReason != null)
            {
                failureReason = $"({nameof(AnonymousFunctionOrDelegateSymbolKey)} {nameof(location)} failed -> {locationFailureReason})";
                return default;
            }
 
            var syntaxTree = location.SourceTree;
            if (syntaxTree == null)
            {
                failureReason = $"({nameof(AnonymousFunctionOrDelegateSymbolKey)} {nameof(SyntaxTree)} failed)";
                return default;
            }
 
            var semanticModel = reader.Compilation.GetSemanticModel(syntaxTree);
            var root = syntaxTree.GetRoot(reader.CancellationToken);
            var node = root.FindNode(location.SourceSpan, getInnermostNodeForTie: true);
 
            var symbol = semanticModel.GetSymbolInfo(node, reader.CancellationToken)
                                      .GetAnySymbol();
 
            // If this was a key for an anonymous delegate type, then go find the
            // associated delegate for this lambda and return that instead of the 
            // lambda function symbol itself.
            if (isAnonymousDelegateType && symbol is IMethodSymbol methodSymbol)
            {
                var anonymousDelegate = methodSymbol.AssociatedAnonymousDelegate;
                symbol = anonymousDelegate;
            }
 
            failureReason = null;
            return new SymbolKeyResolution(symbol);
        }
    }
}