File: CompilationContext.cs
Web Access
Project: ..\..\..\src\ExpressionEvaluator\CSharp\Source\ExpressionCompiler\Microsoft.CodeAnalysis.CSharp.ExpressionCompiler.csproj (Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.ExpressionCompiler)
// 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 Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Debugging;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.ExpressionEvaluator;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.VisualStudio.Debugger.Clr;
using Microsoft.VisualStudio.Debugger.Evaluation.ClrCompilation;
using Roslyn.Utilities;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
 
namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator
{
    internal sealed class CompilationContext
    {
        internal readonly CSharpCompilation Compilation;
        internal readonly Binder NamespaceBinder; // Internal for test purposes.
 
        private readonly MethodSymbol _currentFrame;
        private readonly ImmutableArray<LocalSymbol> _locals;
        private readonly ImmutableDictionary<string, DisplayClassVariable> _displayClassVariables;
        private readonly ImmutableArray<string> _sourceMethodParametersInOrder;
 
        /// <summary>
        /// Display class variables declared outside of the current source method.
        /// They are shadowed by source method parameters and locals declared within the method.
        /// </summary>
        private readonly ImmutableArray<LocalSymbol> _localsForBindingOutside;
 
        /// <summary>
        /// Locals and display class variables declared within the current source method.
        /// They shadow the source method parameters. In other words, display class variables
        /// created for method parameters shadow the parameters.
        /// </summary>
        private readonly ImmutableArray<LocalSymbol> _localsForBindingInside;
 
        private readonly bool _methodNotType;
 
        /// <summary>
        /// Create a context to compile expressions within a method scope.
        /// </summary>
        internal CompilationContext(
            CSharpCompilation compilation,
            MethodSymbol currentFrame,
            MethodSymbol? currentSourceMethod,
            ImmutableArray<LocalSymbol> locals,
            ImmutableSortedSet<int> inScopeHoistedLocalSlots,
            MethodDebugInfo<TypeSymbol, LocalSymbol> methodDebugInfo)
        {
            _currentFrame = currentFrame;
            _methodNotType = !locals.IsDefault;
 
            // NOTE: Since this is done within CompilationContext, it will not be cached.
            // CONSIDER: The values should be the same everywhere in the module, so they
            // could be cached.  
            // (Catch: what happens in a type context without a method def?)
            Compilation = GetCompilationWithExternAliases(compilation, methodDebugInfo.ExternAliasRecords);
 
            // Each expression compile should use a unique compilation
            // to ensure expression-specific synthesized members can be
            // added (anonymous types, for instance).
            Debug.Assert(Compilation != compilation);
 
            // Binder.IsInScopeOfAssociatedSyntaxTree() expects a non-null AssociatedFileIdentifier when
            // looking up file-local types. If there is no document name, use an invalid FilePathChecksumOpt.
            FileIdentifier fileIdentifier = methodDebugInfo.ContainingDocumentName is { } documentName
                ? FileIdentifier.Create(documentName)
                : new FileIdentifier { EncoderFallbackErrorMessage = null, FilePathChecksumOpt = ImmutableArray<byte>.Empty, DisplayFilePath = string.Empty };
 
            NamespaceBinder = CreateBinderChain(
                Compilation,
                currentFrame.ContainingNamespace,
                methodDebugInfo.ImportRecordGroups,
                fileIdentifier: fileIdentifier);
 
            if (_methodNotType)
            {
                _locals = locals;
                _sourceMethodParametersInOrder = GetSourceMethodParametersInOrder(currentFrame, currentSourceMethod);
 
                GetDisplayClassVariables(
                    currentFrame,
                    currentSourceMethod,
                    _locals,
                    inScopeHoistedLocalSlots,
                    isPrimaryConstructor: methodDebugInfo.IsPrimaryConstructor,
                    _sourceMethodParametersInOrder,
                    out var displayClassVariableNamesOutsideInOrder,
                    out var displayClassVariableNamesInsideInOrder,
                    out _displayClassVariables);
 
                Debug.Assert(displayClassVariableNamesOutsideInOrder.Length + displayClassVariableNamesInsideInOrder.Length == _displayClassVariables.Count);
                Debug.Assert(displayClassVariableNamesOutsideInOrder.Concat(displayClassVariableNamesInsideInOrder).Distinct().Length == _displayClassVariables.Count);
                _localsForBindingInside = GetLocalsForBinding(_locals, displayClassVariableNamesInsideInOrder, _displayClassVariables);
                _localsForBindingOutside = GetLocalsForBinding(locals: ImmutableArray<LocalSymbol>.Empty, displayClassVariableNamesOutsideInOrder, _displayClassVariables);
            }
            else
            {
                _locals = ImmutableArray<LocalSymbol>.Empty;
                _displayClassVariables = ImmutableDictionary<string, DisplayClassVariable>.Empty;
                _localsForBindingInside = ImmutableArray<LocalSymbol>.Empty;
                _localsForBindingOutside = ImmutableArray<LocalSymbol>.Empty;
            }
 
            // Assert that the cheap check for "this" is equivalent to the expensive check for "this".
            Debug.Assert(
                (GetThisProxy(_displayClassVariables) != null) ==
                _displayClassVariables.Values.Any(v => v.Kind == DisplayClassVariableKind.This));
        }
 
        internal bool TryCompileExpressions(
            ImmutableArray<CSharpSyntaxNode> syntaxNodes,
            string typeNameBase,
            string methodName,
            DiagnosticBag diagnostics,
            [NotNullWhen(true)] out CommonPEModuleBuilder? module)
        {
            // Create a separate synthesized type for each evaluation method.
            // (Necessary for VB in particular since the EENamedTypeSymbol.Locations
            // is tied to the expression syntax in VB.)
            var synthesizedTypes = syntaxNodes.SelectAsArray(
                (syntax, i, _) => (NamedTypeSymbol)CreateSynthesizedType(syntax, typeNameBase + i, methodName, ImmutableArray<Alias>.Empty),
                arg: (object?)null);
 
            if (synthesizedTypes.Length == 0)
            {
                module = null;
                return false;
            }
 
            module = CreateModuleBuilder(
                Compilation,
                additionalTypes: synthesizedTypes,
                testData: null,
                diagnostics: diagnostics);
 
            Compilation.Compile(
                module,
                emittingPdb: false,
                diagnostics: diagnostics,
                filterOpt: null,
                CancellationToken.None);
 
            return !diagnostics.HasAnyErrors();
        }
 
        internal bool TryCompileExpression(
            CSharpSyntaxNode syntax,
            string typeName,
            string methodName,
            ImmutableArray<Alias> aliases,
            CompilationTestData? testData,
            DiagnosticBag diagnostics,
            [NotNullWhen(true)] out CommonPEModuleBuilder? module,
            [NotNullWhen(true)] out EEMethodSymbol? synthesizedMethod)
        {
            var synthesizedType = CreateSynthesizedType(syntax, typeName, methodName, aliases);
 
            module = CreateModuleBuilder(
                Compilation,
                additionalTypes: ImmutableArray.Create((NamedTypeSymbol)synthesizedType),
                testData,
                diagnostics);
 
            Compilation.Compile(
                module,
                emittingPdb: false,
                diagnostics,
                filterOpt: null,
                CancellationToken.None);
 
            if (diagnostics.HasAnyErrors())
            {
                module = null;
                synthesizedMethod = null;
                return false;
            }
 
            synthesizedMethod = GetSynthesizedMethod(synthesizedType);
            return true;
        }
 
        private EENamedTypeSymbol CreateSynthesizedType(
            CSharpSyntaxNode syntax,
            string typeName,
            string methodName,
            ImmutableArray<Alias> aliases)
        {
            var objectType = Compilation.GetSpecialType(SpecialType.System_Object);
            var synthesizedType = new EENamedTypeSymbol(
                Compilation.SourceModule.GlobalNamespace,
                objectType,
                syntax,
                _currentFrame,
                typeName,
                methodName,
                this,
                (EEMethodSymbol method, DiagnosticBag diags, out ImmutableArray<LocalSymbol> declaredLocals, out ResultProperties properties) =>
                {
                    var hasDisplayClassThis = GetThisProxy(_displayClassVariables) != null;
                    var binder = ExtendBinderChain(
                        syntax,
                        aliases,
                        method,
                        NamespaceBinder,
                        hasDisplayClassThis,
                        _methodNotType,
                        out declaredLocals);
 
                    return (syntax is StatementSyntax statementSyntax)
                        ? BindStatement(binder, statementSyntax, diags, out properties)
                        : BindExpression(binder, (ExpressionSyntax)syntax, diags, out properties);
                });
 
            return synthesizedType;
        }
 
        internal bool TryCompileAssignment(
            ExpressionSyntax syntax,
            string typeName,
            string methodName,
            ImmutableArray<Alias> aliases,
            CompilationTestData? testData,
            DiagnosticBag diagnostics,
            [NotNullWhen(true)] out CommonPEModuleBuilder? module,
            [NotNullWhen(true)] out EEMethodSymbol? synthesizedMethod)
        {
            var objectType = Compilation.GetSpecialType(SpecialType.System_Object);
            var synthesizedType = new EENamedTypeSymbol(
                Compilation.SourceModule.GlobalNamespace,
                objectType,
                syntax,
                _currentFrame,
                typeName,
                methodName,
                this,
                (EEMethodSymbol method, DiagnosticBag diags, out ImmutableArray<LocalSymbol> declaredLocals, out ResultProperties properties) =>
                {
                    var hasDisplayClassThis = GetThisProxy(_displayClassVariables) != null;
                    var binder = ExtendBinderChain(
                        syntax,
                        aliases,
                        method,
                        NamespaceBinder,
                        hasDisplayClassThis,
                        methodNotType: true,
                        out declaredLocals);
 
                    properties = new ResultProperties(DkmClrCompilationResultFlags.PotentialSideEffect);
                    return BindAssignment(binder, syntax, diags);
                });
 
            module = CreateModuleBuilder(
                Compilation,
                additionalTypes: ImmutableArray.Create((NamedTypeSymbol)synthesizedType),
                testData,
                diagnostics);
 
            Compilation.Compile(
                module,
                emittingPdb: false,
                diagnostics,
                filterOpt: null,
                CancellationToken.None);
 
            if (diagnostics.HasAnyErrors())
            {
                module = null;
                synthesizedMethod = null;
                return false;
            }
 
            synthesizedMethod = GetSynthesizedMethod(synthesizedType);
            return true;
        }
 
        private static EEMethodSymbol GetSynthesizedMethod(EENamedTypeSymbol synthesizedType)
            => (EEMethodSymbol)synthesizedType.Methods[0];
 
        private static string GetNextMethodName(ArrayBuilder<MethodSymbol> builder)
            => "<>m" + builder.Count;
 
        /// <summary>
        /// Generate a class containing methods that represent
        /// the set of arguments and locals at the current scope.
        /// </summary>
        internal CommonPEModuleBuilder? CompileGetLocals(
            string typeName,
            ArrayBuilder<LocalAndMethod> localBuilder,
            bool argumentsOnly,
            ImmutableArray<Alias> aliases,
            CompilationTestData? testData,
            DiagnosticBag diagnostics)
        {
            var objectType = Compilation.GetSpecialType(SpecialType.System_Object);
            var allTypeParameters = _currentFrame.GetAllTypeParameters();
            var additionalTypes = ArrayBuilder<NamedTypeSymbol>.GetInstance();
 
            EENamedTypeSymbol? typeVariablesType = null;
            if (!argumentsOnly && allTypeParameters.Length > 0)
            {
                // Generate a generic type with matching type parameters.
                // A null instance of the type will be used to represent the
                // "Type variables" local.
                typeVariablesType = new EENamedTypeSymbol(
                    Compilation.SourceModule.GlobalNamespace,
                    objectType,
                    _currentFrame,
                    ExpressionCompilerConstants.TypeVariablesClassName,
                    (m, t) => ImmutableArray.Create<MethodSymbol>(new EEConstructorSymbol(t)),
                    allTypeParameters,
                    (t1, t2) => allTypeParameters.SelectAsArray((tp, i, t) => (TypeParameterSymbol)new SimpleTypeParameterSymbol(t, i, tp.Name), t2));
                additionalTypes.Add(typeVariablesType);
            }
 
            var synthesizedType = new EENamedTypeSymbol(
                Compilation.SourceModule.GlobalNamespace,
                objectType,
                _currentFrame,
                typeName,
                (m, container) =>
                {
                    var methodBuilder = ArrayBuilder<MethodSymbol>.GetInstance();
 
                    if (!argumentsOnly)
                    {
                        // Pseudo-variables: $exception, $ReturnValue, etc.
                        if (aliases.Length > 0)
                        {
                            var sourceAssembly = Compilation.SourceAssembly;
                            var typeNameDecoder = new EETypeNameDecoder(Compilation, (PEModuleSymbol)_currentFrame.ContainingModule);
                            foreach (var alias in aliases)
                            {
                                if (alias.IsReturnValueWithoutIndex())
                                {
                                    Debug.Assert(aliases.Count(a => a.Kind == DkmClrAliasKind.ReturnValue) > 1);
                                    continue;
                                }
 
                                var local = PlaceholderLocalSymbol.Create(
                                    typeNameDecoder,
                                    _currentFrame,
                                    sourceAssembly,
                                    alias);
 
                                // Skip pseudo-variables with errors.
                                if (local.HasUseSiteError)
                                {
                                    continue;
                                }
 
                                var methodName = GetNextMethodName(methodBuilder);
                                var syntax = SyntaxFactory.IdentifierName(SyntaxFactory.MissingToken(SyntaxKind.IdentifierToken));
                                var aliasMethod = CreateMethod(
                                    container,
                                    methodName,
                                    syntax,
                                    (EEMethodSymbol method, DiagnosticBag diags, out ImmutableArray<LocalSymbol> declaredLocals, out ResultProperties properties) =>
                                    {
                                        declaredLocals = ImmutableArray<LocalSymbol>.Empty;
                                        var expression = new BoundLocal(syntax, local, constantValueOpt: null, type: local.Type);
                                        properties = default;
                                        return new BoundReturnStatement(syntax, RefKind.None, expression, @checked: false) { WasCompilerGenerated = true };
                                    });
                                var flags = local.IsWritableVariable ? DkmClrCompilationResultFlags.None : DkmClrCompilationResultFlags.ReadOnlyResult;
                                localBuilder.Add(MakeLocalAndMethod(local, aliasMethod, flags));
                                methodBuilder.Add(aliasMethod);
                            }
                        }
 
                        // "this" for non-static methods that are not display class methods or
                        // display class methods where the display class contains "<>4__this".
                        if (!m.IsStatic && !IsDisplayClassType(m.ContainingType) ||
                            GetThisProxy(_displayClassVariables) != null)
                        {
                            var methodName = GetNextMethodName(methodBuilder);
                            var method = GetThisMethod(container, methodName);
                            localBuilder.Add(new CSharpLocalAndMethod("this", "this", method, DkmClrCompilationResultFlags.None)); // Note: writable in dev11.
                            methodBuilder.Add(method);
                        }
                    }
 
                    var itemsAdded = PooledHashSet<string>.GetInstance();
 
                    // Method parameters
                    int parameterIndex = m.IsStatic ? 0 : 1;
                    foreach (var parameter in m.Parameters)
                    {
                        var parameterName = parameter.Name;
                        if (GeneratedNameParser.GetKind(parameterName) == GeneratedNameKind.None &&
                            !IsDisplayClassParameter(parameter))
                        {
                            itemsAdded.Add(parameterName);
 
                            // Display class variables created for method parameters shadow the parameters.
                            if (_displayClassVariables.TryGetValue(parameterName, out var variable) && variable.Kind == DisplayClassVariableKind.Parameter)
                            {
                                int saveCount = methodBuilder.Count;
                                int localIndex = 0;
                                foreach (var local in _localsForBindingOutside.Concat(_localsForBindingInside))
                                {
                                    if (local.Name == parameterName && local is EEDisplayClassFieldLocalSymbol)
                                    {
                                        AppendParameterAndMethod(localBuilder, methodBuilder, local, container, localIndex, GetLocalResultFlags(local));
                                        break;
                                    }
 
                                    localIndex++;
                                }
 
                                if (saveCount != methodBuilder.Count)
                                {
                                    continue;
                                }
                            }
 
                            AppendParameterAndMethod(localBuilder, methodBuilder, parameter, container, parameterIndex);
                        }
 
                        parameterIndex++;
                    }
 
                    // In case of iterator or async state machine, the 'm' method has no parameters
                    // but the source method can have parameters to iterate over.
                    if (itemsAdded.Count == 0 && _sourceMethodParametersInOrder.Length != 0)
                    {
                        var localsDictionary = PooledDictionary<string, (LocalSymbol, int)>.GetInstance();
                        int localIndex = 0;
                        foreach (var local in _localsForBindingOutside)
                        {
                            localsDictionary.Add(local.Name, (local, localIndex));
                            localIndex++;
                        }
 
                        foreach (var argumentName in _sourceMethodParametersInOrder)
                        {
                            (LocalSymbol local, int localIndex) localSymbolAndIndex;
                            if (localsDictionary.TryGetValue(argumentName, out localSymbolAndIndex))
                            {
                                itemsAdded.Add(argumentName);
                                var local = localSymbolAndIndex.local;
                                AppendLocalAndMethod(localBuilder, methodBuilder, local, container, localSymbolAndIndex.localIndex, GetLocalResultFlags(local));
                            }
                        }
 
                        localsDictionary.Free();
                    }
 
                    if (!argumentsOnly)
                    {
                        // Locals which were not added as parameters or parameters of the source method.
                        int localIndex = 0;
                        foreach (var local in _localsForBindingOutside)
                        {
                            if (!itemsAdded.Contains(local.Name) &&
                                !_locals.Any(static (l, name) => l.Name == name, local.Name)) // Not captured locals inside the method shadow outside locals
                            {
                                AppendLocalAndMethod(localBuilder, methodBuilder, local, container, localIndex, GetLocalResultFlags(local));
                            }
 
                            localIndex++;
                        }
 
                        foreach (var local in _localsForBindingInside)
                        {
                            if (!itemsAdded.Contains(local.Name))
                            {
                                AppendLocalAndMethod(localBuilder, methodBuilder, local, container, localIndex, GetLocalResultFlags(local));
                            }
 
                            localIndex++;
                        }
 
                        // "Type variables".
                        if (typeVariablesType is object)
                        {
                            var methodName = GetNextMethodName(methodBuilder);
                            var returnType = typeVariablesType.Construct(allTypeParameters.Cast<TypeParameterSymbol, TypeSymbol>());
                            var method = GetTypeVariablesMethod(container, methodName, returnType);
                            localBuilder.Add(new CSharpLocalAndMethod(
                                ExpressionCompilerConstants.TypeVariablesLocalName,
                                ExpressionCompilerConstants.TypeVariablesLocalName,
                                method,
                                DkmClrCompilationResultFlags.ReadOnlyResult));
                            methodBuilder.Add(method);
                        }
                    }
 
                    itemsAdded.Free();
                    return methodBuilder.ToImmutableAndFree();
                });
 
            additionalTypes.Add(synthesizedType);
 
            var module = CreateModuleBuilder(
                Compilation,
                additionalTypes.ToImmutableAndFree(),
                testData,
                diagnostics);
 
            RoslynDebug.AssertNotNull(module);
 
            Compilation.Compile(
                module,
                emittingPdb: false,
                diagnostics,
                filterOpt: null,
                CancellationToken.None);
 
            return diagnostics.HasAnyErrors() ? null : module;
        }
 
        private void AppendLocalAndMethod(
            ArrayBuilder<LocalAndMethod> localBuilder,
            ArrayBuilder<MethodSymbol> methodBuilder,
            LocalSymbol local,
            EENamedTypeSymbol container,
            int localIndex,
            DkmClrCompilationResultFlags resultFlags)
        {
            var methodName = GetNextMethodName(methodBuilder);
            var method = GetLocalMethod(container, methodName, local.Name, localIndex);
            localBuilder.Add(MakeLocalAndMethod(local, method, resultFlags));
            methodBuilder.Add(method);
        }
 
        private void AppendParameterAndMethod(
            ArrayBuilder<LocalAndMethod> localBuilder,
            ArrayBuilder<MethodSymbol> methodBuilder,
            ParameterSymbol parameter,
            EENamedTypeSymbol container,
            int parameterIndex)
        {
            // Note: The native EE doesn't do this, but if we don't escape keyword identifiers,
            // the ResultProvider needs to be able to disambiguate cases like "this" and "@this",
            // which it can't do correctly without semantic information.
            var name = SyntaxHelpers.EscapeKeywordIdentifiers(parameter.Name);
            var methodName = GetNextMethodName(methodBuilder);
            var method = GetParameterMethod(container, methodName, name, parameterIndex);
            localBuilder.Add(new CSharpLocalAndMethod(name, name, method, DkmClrCompilationResultFlags.None));
            methodBuilder.Add(method);
        }
 
        private void AppendParameterAndMethod(
            ArrayBuilder<LocalAndMethod> localBuilder,
            ArrayBuilder<MethodSymbol> methodBuilder,
            LocalSymbol local,
            EENamedTypeSymbol container,
            int localIndex,
            DkmClrCompilationResultFlags resultFlags)
        {
            Debug.Assert(local is EEDisplayClassFieldLocalSymbol && _displayClassVariables[local.Name].Kind == DisplayClassVariableKind.Parameter);
 
            var methodName = GetNextMethodName(methodBuilder);
            // Note: The native EE doesn't do this, but if we don't escape keyword identifiers,
            // the ResultProvider needs to be able to disambiguate cases like "this" and "@this",
            // which it can't do correctly without semantic information.
            var method = GetLocalMethod(container, methodName, SyntaxHelpers.EscapeKeywordIdentifiers(local.Name), localIndex);
            localBuilder.Add(MakeLocalAndMethod(local, method, resultFlags));
            methodBuilder.Add(method);
        }
 
        private static LocalAndMethod MakeLocalAndMethod(LocalSymbol local, MethodSymbol method, DkmClrCompilationResultFlags flags)
        {
            // Note: The native EE doesn't do this, but if we don't escape keyword identifiers,
            // the ResultProvider needs to be able to disambiguate cases like "this" and "@this",
            // which it can't do correctly without semantic information.
            var escapedName = SyntaxHelpers.EscapeKeywordIdentifiers(local.Name);
            var displayName = (local as PlaceholderLocalSymbol)?.DisplayName ?? escapedName;
            return new CSharpLocalAndMethod(escapedName, displayName, method, flags);
        }
 
        private static EEAssemblyBuilder CreateModuleBuilder(
            CSharpCompilation compilation,
            ImmutableArray<NamedTypeSymbol> additionalTypes,
            CompilationTestData? testData,
            DiagnosticBag diagnostics)
        {
            // Each assembly must have a unique name.
            var emitOptions = new EmitOptions(outputNameOverride: ExpressionCompilerUtilities.GenerateUniqueName());
 
            string? runtimeMetadataVersion = compilation.GetRuntimeMetadataVersion(emitOptions, diagnostics);
            var serializationProperties = compilation.ConstructModuleSerializationProperties(emitOptions, runtimeMetadataVersion);
            return new EEAssemblyBuilder(
                compilation.SourceAssembly,
                emitOptions,
                serializationProperties,
                additionalTypes,
                contextType => GetNonDisplayClassContainer(((EENamedTypeSymbol)contextType).SubstitutedSourceType),
                testData);
        }
 
        internal EEMethodSymbol CreateMethod(
            EENamedTypeSymbol container,
            string methodName,
            CSharpSyntaxNode syntax,
            GenerateMethodBody generateMethodBody)
        {
            return new EEMethodSymbol(
                container,
                methodName,
                syntax.Location,
                _currentFrame,
                _locals,
                _localsForBindingOutside,
                _localsForBindingInside,
                _displayClassVariables,
                generateMethodBody);
        }
 
        private EEMethodSymbol GetLocalMethod(EENamedTypeSymbol container, string methodName, string localName, int localIndex)
        {
            var syntax = SyntaxFactory.IdentifierName(localName);
            return CreateMethod(container, methodName, syntax, (EEMethodSymbol method, DiagnosticBag diagnostics, out ImmutableArray<LocalSymbol> declaredLocals, out ResultProperties properties) =>
            {
                declaredLocals = ImmutableArray<LocalSymbol>.Empty;
 
                int indexInside = localIndex - method.LocalsForBindingOutside.Length;
                var local = indexInside >= 0 ? method.LocalsForBindingInside[indexInside] : method.LocalsForBindingOutside[localIndex];
                var expression = new BoundLocal(syntax, local, constantValueOpt: local.GetConstantValue(null, null, new BindingDiagnosticBag(diagnostics)), type: local.Type);
                properties = default;
                return new BoundReturnStatement(syntax, RefKind.None, expression, @checked: false) { WasCompilerGenerated = true };
            });
        }
 
        private EEMethodSymbol GetParameterMethod(EENamedTypeSymbol container, string methodName, string parameterName, int parameterIndex)
        {
            var syntax = SyntaxFactory.IdentifierName(parameterName);
            return CreateMethod(container, methodName, syntax, (EEMethodSymbol method, DiagnosticBag diagnostics, out ImmutableArray<LocalSymbol> declaredLocals, out ResultProperties properties) =>
            {
                declaredLocals = ImmutableArray<LocalSymbol>.Empty;
                var parameter = method.Parameters[parameterIndex];
                var expression = new BoundParameter(syntax, parameter);
                properties = default;
                return new BoundReturnStatement(syntax, RefKind.None, expression, @checked: false) { WasCompilerGenerated = true };
            });
        }
 
        private EEMethodSymbol GetThisMethod(EENamedTypeSymbol container, string methodName)
        {
            var syntax = SyntaxFactory.ThisExpression();
            return CreateMethod(container, methodName, syntax, (EEMethodSymbol method, DiagnosticBag diagnostics, out ImmutableArray<LocalSymbol> declaredLocals, out ResultProperties properties) =>
            {
                declaredLocals = ImmutableArray<LocalSymbol>.Empty;
                var expression = new BoundThisReference(syntax, GetNonDisplayClassContainer(container.SubstitutedSourceType));
                properties = default;
                return new BoundReturnStatement(syntax, RefKind.None, expression, @checked: false) { WasCompilerGenerated = true };
            });
        }
 
        private EEMethodSymbol GetTypeVariablesMethod(EENamedTypeSymbol container, string methodName, NamedTypeSymbol typeVariablesType)
        {
            var syntax = SyntaxFactory.IdentifierName(SyntaxFactory.MissingToken(SyntaxKind.IdentifierToken));
            return CreateMethod(container, methodName, syntax, (EEMethodSymbol method, DiagnosticBag diagnostics, out ImmutableArray<LocalSymbol> declaredLocals, out ResultProperties properties) =>
            {
                declaredLocals = ImmutableArray<LocalSymbol>.Empty;
                var type = method.TypeMap.SubstituteNamedType(typeVariablesType);
                var expression = new BoundObjectCreationExpression(syntax, type.InstanceConstructors[0]);
                var statement = new BoundReturnStatement(syntax, RefKind.None, expression, @checked: false) { WasCompilerGenerated = true };
                properties = default;
                return statement;
            });
        }
 
        private static BoundStatement? BindExpression(Binder binder, ExpressionSyntax syntax, DiagnosticBag diagnostics, out ResultProperties resultProperties)
        {
            var flags = DkmClrCompilationResultFlags.None;
            var bindingDiagnostics = new BindingDiagnosticBag(diagnostics);
 
            // In addition to C# expressions, the native EE also supports
            // type names which are bound to a representation of the type
            // (but not System.Type) that the user can expand to see the
            // base type. Instead, we only allow valid C# expressions.
            var expression = IsDeconstruction(syntax)
                ? binder.BindDeconstruction((AssignmentExpressionSyntax)syntax, bindingDiagnostics, resultIsUsedOverride: true)
                : binder.BindRValueWithoutTargetType(syntax, bindingDiagnostics);
            if (diagnostics.HasAnyErrors())
            {
                resultProperties = default;
                return null;
            }
 
            try
            {
                if (MayHaveSideEffectsVisitor.MayHaveSideEffects(expression))
                {
                    flags |= DkmClrCompilationResultFlags.PotentialSideEffect;
                }
            }
            catch (BoundTreeVisitor.CancelledByStackGuardException ex)
            {
                ex.AddAnError(diagnostics);
                resultProperties = default;
                return null;
            }
 
            var expressionType = expression.Type;
            if (expressionType is null)
            {
                expression = binder.CreateReturnConversion(
                    syntax,
                    bindingDiagnostics,
                    expression,
                    RefKind.None,
                    binder.Compilation.GetSpecialType(SpecialType.System_Object));
                if (diagnostics.HasAnyErrors())
                {
                    resultProperties = default;
                    return null;
                }
            }
            else if (expressionType.SpecialType == SpecialType.System_Void)
            {
                flags |= DkmClrCompilationResultFlags.ReadOnlyResult;
                Debug.Assert(expression.ConstantValueOpt == null);
                resultProperties = expression.ExpressionSymbol.GetResultProperties(flags, isConstant: false);
                return new BoundExpressionStatement(syntax, expression) { WasCompilerGenerated = true };
            }
            else if (expressionType.SpecialType == SpecialType.System_Boolean)
            {
                flags |= DkmClrCompilationResultFlags.BoolResult;
            }
 
            if (!IsAssignableExpression(binder, expression))
            {
                flags |= DkmClrCompilationResultFlags.ReadOnlyResult;
            }
 
            resultProperties = expression.ExpressionSymbol.GetResultProperties(flags, expression.ConstantValueOpt != null);
            return new BoundReturnStatement(syntax, RefKind.None, expression, @checked: false) { WasCompilerGenerated = true };
        }
 
        private static bool IsDeconstruction(ExpressionSyntax syntax)
        {
            if (syntax.Kind() != SyntaxKind.SimpleAssignmentExpression)
            {
                return false;
            }
 
            var node = (AssignmentExpressionSyntax)syntax;
            return node.Left.Kind() == SyntaxKind.TupleExpression || node.Left.Kind() == SyntaxKind.DeclarationExpression;
        }
 
        private static BoundStatement BindStatement(Binder binder, StatementSyntax syntax, DiagnosticBag diagnostics, out ResultProperties properties)
        {
            properties = new ResultProperties(DkmClrCompilationResultFlags.PotentialSideEffect | DkmClrCompilationResultFlags.ReadOnlyResult);
            return binder.BindStatement(syntax, new BindingDiagnosticBag(diagnostics));
        }
 
        private static bool IsAssignableExpression(Binder binder, BoundExpression expression)
        {
            var result = binder.CheckValueKind(expression.Syntax, expression, Binder.BindValueKind.Assignable, checkingReceiver: false, BindingDiagnosticBag.Discarded);
            return result;
        }
 
        private static BoundStatement? BindAssignment(Binder binder, ExpressionSyntax syntax, DiagnosticBag diagnostics)
        {
            var expression = binder.BindValue(syntax, new BindingDiagnosticBag(diagnostics), Binder.BindValueKind.RValue);
            if (diagnostics.HasAnyErrors())
            {
                return null;
            }
 
            return new BoundExpressionStatement(expression.Syntax, expression) { WasCompilerGenerated = true };
        }
 
        private static Binder CreateBinderChain(
            CSharpCompilation compilation,
            NamespaceSymbol @namespace,
            ImmutableArray<ImmutableArray<ImportRecord>> importRecordGroups,
            FileIdentifier? fileIdentifier)
        {
            var stack = ArrayBuilder<string>.GetInstance();
            while (@namespace is object)
            {
                stack.Push(@namespace.Name);
                @namespace = @namespace.ContainingNamespace;
            }
 
            Binder binder = new BuckStopsHereBinder(compilation, fileIdentifier);
            var hasImports = !importRecordGroups.IsDefaultOrEmpty;
            var numImportStringGroups = hasImports ? importRecordGroups.Length : 0;
            var currentStringGroup = numImportStringGroups - 1;
 
            // PERF: We used to call compilation.GetCompilationNamespace on every iteration,
            // but that involved walking up to the global namespace, which we have to do
            // anyway.  Instead, we'll inline the functionality into our own walk of the
            // namespace chain.
            @namespace = compilation.GlobalNamespace;
 
            while (stack.Count > 0)
            {
                var namespaceName = stack.Pop();
                if (namespaceName.Length > 0)
                {
                    // We're re-getting the namespace, rather than using the one containing
                    // the current frame method, because we want the merged namespace.
                    @namespace = @namespace.GetNestedNamespace(namespaceName);
                    RoslynDebug.AssertNotNull(@namespace);
                }
                else
                {
                    Debug.Assert((object)@namespace == compilation.GlobalNamespace);
                }
 
                if (hasImports)
                {
                    if (currentStringGroup < 0)
                    {
                        Debug.WriteLine($"No import string group for namespace '{@namespace}'");
                        break;
                    }
 
                    var importsBinder = new InContainerBinder(@namespace, binder);
                    Imports imports = BuildImports(compilation, importRecordGroups[currentStringGroup], importsBinder);
                    currentStringGroup--;
 
                    binder = WithExternAndUsingAliasesBinder.Create(imports.ExternAliases, imports.UsingAliases, WithUsingNamespacesAndTypesBinder.Create(imports.Usings, binder));
                }
 
                binder = new InContainerBinder(@namespace, binder);
            }
 
            stack.Free();
 
            if (currentStringGroup >= 0)
            {
                // CONSIDER: We could lump these into the outermost namespace.  It's probably not worthwhile since
                // the usings are already for the wrong method.
                Debug.WriteLine($"Found {currentStringGroup + 1} import string groups without corresponding namespaces");
            }
 
            return binder;
        }
 
        private static CSharpCompilation GetCompilationWithExternAliases(CSharpCompilation compilation, ImmutableArray<ExternAliasRecord> externAliasRecords)
        {
            if (externAliasRecords.IsDefaultOrEmpty)
            {
                return compilation.Clone();
            }
 
            var updatedReferences = ArrayBuilder<MetadataReference>.GetInstance();
            var assembliesAndModulesBuilder = ArrayBuilder<Symbol>.GetInstance();
            foreach (var reference in compilation.References)
            {
                updatedReferences.Add(reference);
                assembliesAndModulesBuilder.Add(compilation.GetAssemblyOrModuleSymbol(reference)!);
            }
            Debug.Assert(assembliesAndModulesBuilder.Count == updatedReferences.Count);
 
            var assembliesAndModules = assembliesAndModulesBuilder.ToImmutableAndFree();
 
            foreach (var externAliasRecord in externAliasRecords)
            {
                var targetAssembly = externAliasRecord.TargetAssembly as AssemblySymbol;
                int index;
                if (targetAssembly != null)
                {
                    index = assembliesAndModules.IndexOf(targetAssembly);
                }
                else
                {
                    index = IndexOfMatchingAssembly((AssemblyIdentity)externAliasRecord.TargetAssembly, assembliesAndModules, compilation.Options.AssemblyIdentityComparer);
                }
 
                if (index < 0)
                {
                    Debug.WriteLine($"Unable to find corresponding assembly reference for extern alias '{externAliasRecord}'");
                    continue;
                }
 
                var externAlias = externAliasRecord.Alias;
 
                var assemblyReference = updatedReferences[index];
                var oldAliases = assemblyReference.Properties.Aliases;
                var newAliases = oldAliases.IsEmpty
                    ? ImmutableArray.Create(MetadataReferenceProperties.GlobalAlias, externAlias)
                    : oldAliases.Concat(ImmutableArray.Create(externAlias));
 
                // NOTE: Dev12 didn't emit custom debug info about "global", so we don't have
                // a good way to distinguish between a module aliased with both (e.g.) "X" and 
                // "global" and a module aliased with only "X".  As in Dev12, we assume that 
                // "global" is a valid alias to remain at least as permissive as source.
                // NOTE: In the event that this introduces ambiguities between two assemblies
                // (e.g. because one was "global" in source and the other was "X"), it should be
                // possible to disambiguate as long as each assembly has a distinct extern alias,
                // not necessarily used in source.
                Debug.Assert(newAliases.Contains(MetadataReferenceProperties.GlobalAlias));
 
                // Replace the value in the map with the updated reference.
                updatedReferences[index] = assemblyReference.WithAliases(newAliases);
            }
 
            compilation = compilation.WithReferences(updatedReferences);
 
            updatedReferences.Free();
 
            return compilation;
        }
 
        private static int IndexOfMatchingAssembly(AssemblyIdentity referenceIdentity, ImmutableArray<Symbol> assembliesAndModules, AssemblyIdentityComparer assemblyIdentityComparer)
        {
            for (int i = 0; i < assembliesAndModules.Length; i++)
            {
                if (assembliesAndModules[i] is AssemblySymbol assembly && assemblyIdentityComparer.ReferenceMatchesDefinition(referenceIdentity, assembly.Identity))
                {
                    return i;
                }
            }
 
            return -1;
        }
 
        private static Binder ExtendBinderChain(
            CSharpSyntaxNode syntax,
            ImmutableArray<Alias> aliases,
            EEMethodSymbol method,
            Binder binder,
            bool hasDisplayClassThis,
            bool methodNotType,
            out ImmutableArray<LocalSymbol> declaredLocals)
        {
            var substitutedSourceMethod = GetSubstitutedSourceMethod(method.SubstitutedSourceMethod, hasDisplayClassThis);
            var substitutedSourceType = substitutedSourceMethod.ContainingType;
 
            var stack = ArrayBuilder<NamedTypeSymbol>.GetInstance();
            for (var type = substitutedSourceType; type is object; type = type.ContainingType)
            {
                stack.Add(type);
            }
 
            while (stack.Count > 0)
            {
                substitutedSourceType = stack.Pop();
 
                binder = new InContainerBinder(substitutedSourceType, binder);
                if (substitutedSourceType.Arity > 0)
                {
                    binder = new WithTypeArgumentsBinder(substitutedSourceType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics, binder);
                }
            }
 
            stack.Free();
 
            if (substitutedSourceMethod.Arity > 0)
            {
                binder = new WithTypeArgumentsBinder(substitutedSourceMethod.TypeArgumentsWithAnnotations, binder);
            }
 
            // Method locals and parameters shadow pseudo-variables.
            // That is why we place PlaceholderLocalBinder and ExecutableCodeBinder before EEMethodBinder.
            if (methodNotType)
            {
                var typeNameDecoder = new EETypeNameDecoder(binder.Compilation, (PEModuleSymbol)substitutedSourceMethod.ContainingModule);
                binder = new PlaceholderLocalBinder(
                    syntax,
                    aliases,
                    method,
                    typeNameDecoder,
                    binder);
 
                binder = new SimpleLocalScopeBinder(method.LocalsForBindingOutside, binder);
            }
 
            binder = new EEMethodBinder(method, substitutedSourceMethod, binder);
 
            if (methodNotType)
            {
                binder = new SimpleLocalScopeBinder(method.LocalsForBindingInside, binder);
            }
 
            Binder? actualRootBinder = null;
            SyntaxNode? declaredLocalsScopeDesignator = null;
 
            var executableBinder = new ExecutableCodeBinder(syntax, substitutedSourceMethod, binder,
                (rootBinder, declaredLocalsScopeDesignatorOpt) =>
                {
                    actualRootBinder = rootBinder;
                    declaredLocalsScopeDesignator = declaredLocalsScopeDesignatorOpt;
                });
 
            // We just need to trigger the process of building the binder map
            // so that the lambda above was executed.
            executableBinder.GetBinder(syntax);
 
            RoslynDebug.AssertNotNull(actualRootBinder);
 
            if (declaredLocalsScopeDesignator != null)
            {
                declaredLocals = actualRootBinder.GetDeclaredLocalsForScope(declaredLocalsScopeDesignator);
            }
            else
            {
                declaredLocals = ImmutableArray<LocalSymbol>.Empty;
            }
 
            return actualRootBinder;
        }
 
        private static Imports BuildImports(CSharpCompilation compilation, ImmutableArray<ImportRecord> importRecords, InContainerBinder binder)
        {
            // We make a first pass to extract all of the extern aliases because other imports may depend on them.
            var externsBuilder = ArrayBuilder<AliasAndExternAliasDirective>.GetInstance();
            foreach (var importRecord in importRecords)
            {
                if (importRecord.TargetKind != ImportTargetKind.Assembly)
                {
                    continue;
                }
 
                var alias = importRecord.Alias;
                RoslynDebug.AssertNotNull(alias);
 
                if (!TryParseIdentifierNameSyntax(alias, out var aliasNameSyntax))
                {
                    Debug.WriteLine($"Import record '{importRecord}' has syntactically invalid extern alias '{alias}'");
                    continue;
                }
 
                NamespaceSymbol target;
                compilation.GetExternAliasTarget(aliasNameSyntax.Identifier.ValueText, out target);
                Debug.Assert(target.IsGlobalNamespace);
 
                var aliasSymbol = AliasSymbol.CreateCustomDebugInfoAlias(target, aliasNameSyntax.Identifier, binder.ContainingMemberOrLambda, isExtern: true);
                externsBuilder.Add(new AliasAndExternAliasDirective(aliasSymbol, externAliasDirective: null, skipInLookup: false));
            }
 
            var externs = externsBuilder.ToImmutableAndFree();
 
            if (externs.Any())
            {
                // NB: This binder (and corresponding Imports) is only used to bind the other imports.
                // We'll merge the externs into a final Imports object and return that to be used in
                // the actual binder chain.
                binder = new InContainerBinder(
                    binder.Container,
                    WithExternAliasesBinder.Create(externs, binder));
            }
 
            var usingAliases = ImmutableDictionary.CreateBuilder<string, AliasAndUsingDirective>();
            var usingsBuilder = ArrayBuilder<NamespaceOrTypeAndUsingDirective>.GetInstance();
 
            foreach (var importRecord in importRecords)
            {
                switch (importRecord.TargetKind)
                {
                    case ImportTargetKind.Type:
                        {
                            var typeSymbol = (TypeSymbol?)importRecord.TargetType;
                            RoslynDebug.AssertNotNull(typeSymbol);
 
                            if (typeSymbol.IsErrorType())
                            {
                                // Type is unrecognized. The import may have been
                                // valid in the original source but unnecessary.
                                continue; // Don't add anything for this import.
                            }
                            else if (importRecord.Alias == null && !typeSymbol.IsStatic)
                            {
                                // Only static types can be directly imported.
                                continue;
                            }
 
                            if (!TryAddImport(importRecord.Alias, typeSymbol, usingsBuilder, usingAliases, binder, importRecord))
                            {
                                continue;
                            }
 
                            break;
                        }
 
                    case ImportTargetKind.Namespace:
                        {
                            var namespaceName = importRecord.TargetString;
                            RoslynDebug.AssertNotNull(namespaceName);
 
                            if (!SyntaxHelpers.TryParseDottedName(namespaceName, out _))
                            {
                                // DevDiv #999086: Some previous version of VS apparently generated type aliases as "UA{alias} T{alias-qualified type name}". 
                                // Neither Roslyn nor Dev12 parses such imports.  However, Roslyn discards them, rather than interpreting them as "UA{alias}"
                                // (which will rarely work and never be correct).
                                Debug.WriteLine($"Import record '{importRecord}' has syntactically invalid target '{importRecord.TargetString}'");
                                continue;
                            }
 
                            NamespaceSymbol globalNamespace;
                            var targetAssembly = (AssemblySymbol?)importRecord.TargetAssembly;
 
                            if (targetAssembly is object)
                            {
                                if (targetAssembly.IsMissing)
                                {
                                    Debug.WriteLine($"Import record '{importRecord}' has invalid assembly reference '{targetAssembly.Identity}'");
                                    continue;
                                }
 
                                globalNamespace = targetAssembly.GlobalNamespace;
                            }
                            else if (importRecord.TargetAssemblyAlias != null)
                            {
                                if (!TryParseIdentifierNameSyntax(importRecord.TargetAssemblyAlias, out var externAliasSyntax))
                                {
                                    Debug.WriteLine($"Import record '{importRecord}' has syntactically invalid extern alias '{importRecord.TargetAssemblyAlias}'");
                                    continue;
                                }
 
                                var aliasSymbol = (AliasSymbol)binder.BindNamespaceAliasSymbol(externAliasSyntax, BindingDiagnosticBag.Discarded);
 
                                if (aliasSymbol is null)
                                {
                                    Debug.WriteLine($"Import record '{importRecord}' requires unknown extern alias '{importRecord.TargetAssemblyAlias}'");
                                    continue;
                                }
 
                                globalNamespace = (NamespaceSymbol)aliasSymbol.Target;
                            }
                            else
                            {
                                globalNamespace = compilation.GlobalNamespace;
                            }
 
                            var namespaceSymbol = BindNamespace(namespaceName, globalNamespace);
 
                            if (namespaceSymbol is null)
                            {
                                // Namespace is unrecognized. The import may have been
                                // valid in the original source but unnecessary.
                                continue; // Don't add anything for this import.
                            }
 
                            if (!TryAddImport(importRecord.Alias, namespaceSymbol, usingsBuilder, usingAliases, binder, importRecord))
                            {
                                continue;
                            }
 
                            break;
                        }
 
                    case ImportTargetKind.Assembly:
                        // Handled in first pass (above).
                        break;
 
                    default:
                        throw ExceptionUtilities.UnexpectedValue(importRecord.TargetKind);
                }
            }
 
            return Imports.Create(usingAliases.ToImmutableDictionary(), usingsBuilder.ToImmutableAndFree(), externs);
        }
 
        private static NamespaceSymbol? BindNamespace(string namespaceName, NamespaceSymbol globalNamespace)
        {
            NamespaceSymbol? namespaceSymbol = globalNamespace;
            foreach (var name in namespaceName.Split('.'))
            {
                var members = namespaceSymbol.GetMembers(name);
                namespaceSymbol = (members.Length == 1) ? members[0] as NamespaceSymbol : null;
 
                if (namespaceSymbol is null)
                {
                    break;
                }
            }
 
            return namespaceSymbol;
        }
 
        private static bool TryAddImport(
            string? alias,
            NamespaceOrTypeSymbol targetSymbol,
            ArrayBuilder<NamespaceOrTypeAndUsingDirective> usingsBuilder,
            ImmutableDictionary<string, AliasAndUsingDirective>.Builder usingAliases,
            InContainerBinder binder,
            ImportRecord importRecord)
        {
            if (alias == null)
            {
                usingsBuilder.Add(new NamespaceOrTypeAndUsingDirective(targetSymbol, usingDirective: null, dependencies: default));
            }
            else
            {
                if (!TryParseIdentifierNameSyntax(alias, out var aliasSyntax))
                {
                    Debug.WriteLine($"Import record '{importRecord}' has syntactically invalid alias '{alias}'");
                    return false;
                }
 
                var aliasSymbol = AliasSymbol.CreateCustomDebugInfoAlias(targetSymbol, aliasSyntax.Identifier, binder.ContainingMemberOrLambda, isExtern: false);
                usingAliases.Add(alias, new AliasAndUsingDirective(aliasSymbol, usingDirective: null));
            }
 
            return true;
        }
 
        private static bool TryParseIdentifierNameSyntax(string name, [NotNullWhen(true)] out IdentifierNameSyntax? syntax)
        {
            if (name == MetadataReferenceProperties.GlobalAlias)
            {
                syntax = SyntaxFactory.IdentifierName(SyntaxFactory.Token(SyntaxKind.GlobalKeyword));
                return true;
            }
 
            if (!SyntaxHelpers.TryParseDottedName(name, out var nameSyntax) || nameSyntax.Kind() != SyntaxKind.IdentifierName)
            {
                syntax = null;
                return false;
            }
 
            syntax = (IdentifierNameSyntax)nameSyntax;
            return true;
        }
 
        internal CommonMessageProvider MessageProvider
        {
            get { return Compilation.MessageProvider; }
        }
 
        private static DkmClrCompilationResultFlags GetLocalResultFlags(LocalSymbol local)
        {
            // CONSIDER: We might want to prevent the user from modifying pinned locals -
            // that's pretty dangerous.
            return local.IsConst
                ? DkmClrCompilationResultFlags.ReadOnlyResult
                : DkmClrCompilationResultFlags.None;
        }
 
        /// <summary>
        /// Generate the set of locals to use for binding. 
        /// </summary>
        private static ImmutableArray<LocalSymbol> GetLocalsForBinding(
            ImmutableArray<LocalSymbol> locals,
            ImmutableArray<string> displayClassVariableNamesInOrder,
            ImmutableDictionary<string, DisplayClassVariable> displayClassVariables)
        {
            var builder = ArrayBuilder<LocalSymbol>.GetInstance();
            foreach (var local in locals)
            {
                var name = local.Name;
                if (name == null)
                {
                    continue;
                }
 
                if (GeneratedNameParser.GetKind(name) != GeneratedNameKind.None)
                {
                    continue;
                }
 
                // Although Roslyn doesn't name synthesized locals unless they are well-known to EE,
                // Dev12 did so we need to skip them here.
                if (GeneratedNameParser.IsSynthesizedLocalName(name))
                {
                    continue;
                }
 
                builder.Add(local);
            }
 
            foreach (var variableName in displayClassVariableNamesInOrder)
            {
                var variable = displayClassVariables[variableName];
                switch (variable.Kind)
                {
                    case DisplayClassVariableKind.Local:
                    case DisplayClassVariableKind.Parameter:
                        builder.Add(new EEDisplayClassFieldLocalSymbol(variable));
                        break;
                }
            }
 
            return builder.ToImmutableAndFree();
        }
 
        private static ImmutableArray<string> GetSourceMethodParametersInOrder(
            MethodSymbol method,
            MethodSymbol? sourceMethod)
        {
            var containingType = method.ContainingType;
            bool isIteratorOrAsyncMethod = IsDisplayClassType(containingType) &&
                GeneratedNameParser.GetKind(containingType.Name) == GeneratedNameKind.StateMachineType;
 
            var parameterNamesInOrder = ArrayBuilder<string>.GetInstance();
            // For version before .NET 4.5, we cannot find the sourceMethod properly:
            // The source method coincides with the original method in the case.
            // Therefore, for iterators and async state machines, we have to get parameters from the containingType.
            // This does not guarantee the proper order of parameters.
            if (isIteratorOrAsyncMethod && method == sourceMethod)
            {
                Debug.Assert(IsDisplayClassType(containingType));
                foreach (var member in containingType.GetMembers())
                {
                    if (member.Kind != SymbolKind.Field)
                    {
                        continue;
                    }
 
                    var field = (FieldSymbol)member;
                    var fieldName = field.Name;
                    if (GeneratedNameParser.GetKind(fieldName) == GeneratedNameKind.None)
                    {
                        parameterNamesInOrder.Add(fieldName);
                    }
                }
            }
            else
            {
                if (sourceMethod is object)
                {
                    foreach (var p in sourceMethod.Parameters)
                    {
                        var parameterName = p.Name;
 
                        if (GeneratedNameParser.GetKind(parameterName) == GeneratedNameKind.None &&
                            !IsDisplayClassParameter(p))
                        {
                            parameterNamesInOrder.Add(parameterName);
                        }
                    }
                }
            }
 
            return parameterNamesInOrder.ToImmutableAndFree();
        }
 
        /// <summary>
        /// Return a mapping of captured variables (parameters, locals, and
        /// "this") to locals. The mapping is needed to expose the original
        /// local identifiers (those from source) in the binder.
        /// </summary>
        private static void GetDisplayClassVariables(
            MethodSymbol currentFrame,
            MethodSymbol? currentSourceMethod,
            ImmutableArray<LocalSymbol> locals,
            ImmutableSortedSet<int> inScopeHoistedLocalSlots,
            bool isPrimaryConstructor,
            ImmutableArray<string> parameterNamesInOrder,
            out ImmutableArray<string> displayClassVariableNamesOutsideInOrder,
            out ImmutableArray<string> displayClassVariableNamesInsideInOrder,
            out ImmutableDictionary<string, DisplayClassVariable> displayClassVariables)
        {
            // Calculate the shortest paths from locals to instances of display
            // classes. There should not be two instances of the same display
            // class immediately within any particular method.
            var displayClassInstancesOutside = ArrayBuilder<DisplayClassInstanceAndFields>.GetInstance();
 
            foreach (var parameter in currentFrame.Parameters)
            {
                if (GeneratedNameParser.GetKind(parameter.Name) == GeneratedNameKind.TransparentIdentifier ||
                    IsDisplayClassParameter(parameter))
                {
                    var instance = new DisplayClassInstanceFromParameter(parameter);
                    displayClassInstancesOutside.Add(new DisplayClassInstanceAndFields(instance));
                }
            }
 
            if (IsDisplayClassType(currentFrame.ContainingType) && !currentFrame.IsStatic)
            {
                // Add "this" display class instance.
                var instance = new DisplayClassInstanceFromParameter(currentFrame.ThisParameter);
                displayClassInstancesOutside.Add(new DisplayClassInstanceAndFields(instance));
            }
 
            var displayClassTypes = PooledHashSet<TypeSymbol>.GetInstance();
            foreach (var instance in displayClassInstancesOutside)
            {
                displayClassTypes.Add(instance.Instance.Type);
            }
 
            // Find any additional display class instances.
            GetAdditionalDisplayClassInstances(displayClassTypes, displayClassInstancesOutside);
 
            // Add any display class instances from locals (these will contain any hoisted locals).
            // Locals are only added after finding all display class instances reachable from
            // parameters because locals may be null (temporary locals in async state machine
            // for instance) so we prefer parameters to locals.
            var displayClassInstancesInside = ArrayBuilder<DisplayClassInstanceAndFields>.GetInstance();
            foreach (var local in locals)
            {
                var name = local.Name;
                if (name != null && GeneratedNameParser.GetKind(name) == GeneratedNameKind.DisplayClassLocalOrField)
                {
                    var localType = local.Type;
                    if (localType is object && displayClassTypes.Add(localType))
                    {
                        var instance = new DisplayClassInstanceFromLocal((EELocalSymbol)local);
                        displayClassInstancesInside.Add(new DisplayClassInstanceAndFields(instance));
                    }
                }
            }
            GetAdditionalDisplayClassInstances(displayClassTypes, displayClassInstancesInside);
 
            displayClassTypes.Free();
 
            // This is a special handling for async MoveNext method.
            // Parameters are not declared by it, and, therefore, display variables corresponding to them will be those declared outside.  
            bool parametersAreOutside = currentFrame.ParameterCount == 0 && !parameterNamesInOrder.IsEmpty;
 
            var displayClassVariablesBuilder = PooledDictionary<string, DisplayClassVariable>.GetInstance();
            var displayClassVariableNamesOutsideInOrderBuilder = ArrayBuilder<string>.GetInstance();
            var displayClassVariableNamesInsideInOrderBuilder = ArrayBuilder<string>.GetInstance();
 
            // Locals inside shadow locals outside
            buildResult(displayClassInstancesInside, inScopeHoistedLocalSlots, parametersAreOutside ? ImmutableArray<string>.Empty : parameterNamesInOrder, displayClassVariablesBuilder, displayClassVariableNamesInsideInOrderBuilder);
 
            // Let's add captured Primary Constructor parameters.
            bool checkForPrimaryConstructor = true;
 
            if (!currentFrame.IsStatic && isPrimaryConstructor)
            {
                checkForPrimaryConstructor = !tryAddCapturedPrimaryConstructorParameters(currentFrame, shadowingParameterNames: ImmutableArray<string>.Empty,
                                                                                         possiblyCapturingType: currentFrame.ContainingType,
                                                                                         possiblyCapturingTypeInstance: (Instance: null, Fields: ConsList<FieldSymbol>.Empty),
                                                                                         displayClassVariablesBuilder, displayClassVariableNamesInsideInOrderBuilder);
            }
 
            buildResult(displayClassInstancesOutside, inScopeHoistedLocalSlots, parametersAreOutside ? parameterNamesInOrder : ImmutableArray<string>.Empty, displayClassVariablesBuilder, displayClassVariableNamesOutsideInOrderBuilder);
 
            // ExtendBinderChain will place Primary Constructor parameters added below below InContainerBinder, rather than above it.
            // However, since they are captured, they were not shadowed by any member at compile time.
            // In theory, a shadowing member could be added into a base class after the build,
            // but it is probably fine to shadow that member in EE. The member could still be accessed
            // with qualification, but there wouldn't be a way to access captured parameter
            // if we were to shadow it.
            if (!isPrimaryConstructor && checkForPrimaryConstructor && currentFrame == currentSourceMethod && !currentFrame.IsStatic)
            {
                checkForPrimaryConstructor = !tryAddCapturedPrimaryConstructorParameters(currentFrame, shadowingParameterNames: parameterNamesInOrder,
                                                                                         possiblyCapturingType: currentFrame.ContainingType,
                                                                                         possiblyCapturingTypeInstance: (Instance: null, Fields: ConsList<FieldSymbol>.Empty),
                                                                                         displayClassVariablesBuilder, displayClassVariableNamesOutsideInOrderBuilder);
            }
 
            if (checkForPrimaryConstructor && displayClassVariablesBuilder.Values.FirstOrDefault(v => v.Kind == DisplayClassVariableKind.This) is { } thisProxy)
            {
                tryAddCapturedPrimaryConstructorParameters(currentFrame, shadowingParameterNames: parameterNamesInOrder, possiblyCapturingType: thisProxy.Type,
                                                           possiblyCapturingTypeInstance: (Instance: thisProxy.DisplayClassInstance, Fields: thisProxy.DisplayClassFields),
                                                           displayClassVariablesBuilder, displayClassVariableNamesOutsideInOrderBuilder);
            }
 
            displayClassVariables = displayClassVariablesBuilder.ToImmutableDictionary();
            displayClassVariablesBuilder.Free();
 
            displayClassVariableNamesOutsideInOrder = displayClassVariableNamesOutsideInOrderBuilder.ToImmutableAndFree();
            displayClassVariableNamesInsideInOrder = displayClassVariableNamesInsideInOrderBuilder.ToImmutableAndFree();
 
            displayClassInstancesOutside.Free();
            displayClassInstancesInside.Free();
 
            static void buildResult(
                ArrayBuilder<DisplayClassInstanceAndFields> displayClassInstances,
                ImmutableSortedSet<int> inScopeHoistedLocalSlots,
                ImmutableArray<string> parameterNamesInOrder,
                Dictionary<string, DisplayClassVariable> displayClassVariablesBuilder,
                ArrayBuilder<string> displayClassVariableNamesInOrderBuilder)
            {
                if (displayClassInstances.Any())
                {
                    var parameterNames = PooledHashSet<string>.GetInstance();
                    foreach (var name in parameterNamesInOrder)
                    {
                        parameterNames.Add(name);
                    }
 
                    // The locals are the set of all fields from the display classes.
                    foreach (var instance in displayClassInstances)
                    {
                        GetDisplayClassVariables(
                            displayClassVariableNamesInOrderBuilder,
                            displayClassVariablesBuilder,
                            parameterNames,
                            inScopeHoistedLocalSlots,
                            instance);
                    }
 
                    parameterNames.Free();
                }
            }
 
            static bool tryAddCapturedPrimaryConstructorParameters(
                MethodSymbol currentFrame,
                ImmutableArray<string> shadowingParameterNames,
                TypeSymbol possiblyCapturingType,
                (DisplayClassInstance? Instance, ConsList<FieldSymbol> Fields) possiblyCapturingTypeInstance,
                PooledDictionary<string, DisplayClassVariable> displayClassVariablesBuilder,
                ArrayBuilder<string> displayClassVariableNamesInOrderBuilder)
            {
                bool sawCapturedParameters = false;
 
                foreach (var field in possiblyCapturingType.GetMembers().OfType<FieldSymbol>())
                {
                    if (!field.IsStatic && GeneratedNameParser.TryParsePrimaryConstructorParameterFieldName(field.Name, out string? parameterName))
                    {
                        sawCapturedParameters = true;
 
                        if (!displayClassVariablesBuilder.ContainsKey(parameterName) &&
                            !shadowingParameterNames.Contains(parameterName))
                        {
                            if (possiblyCapturingTypeInstance.Instance is null)
                            {
                                Debug.Assert((object)possiblyCapturingType == currentFrame.ContainingType);
                                Debug.Assert(possiblyCapturingTypeInstance.Fields.IsEmpty());
                                possiblyCapturingTypeInstance.Instance = new DisplayClassInstanceFromParameter(currentFrame.ThisParameter);
                            }
 
                            DisplayClassVariable variable = new DisplayClassVariable(parameterName, DisplayClassVariableKind.Parameter,
                                                                                     possiblyCapturingTypeInstance.Instance,
                                                                                     possiblyCapturingTypeInstance.Fields.Prepend(field));
 
                            displayClassVariablesBuilder.Add(parameterName, variable);
                            displayClassVariableNamesInOrderBuilder.Add(parameterName);
                        }
                    }
                }
 
                return sawCapturedParameters;
            }
        }
 
        private static void GetAdditionalDisplayClassInstances(
            HashSet<TypeSymbol> displayClassTypes,
            ArrayBuilder<DisplayClassInstanceAndFields> displayClassInstances)
        {
            // Find any additional display class instances breadth first.
            for (int i = 0; i < displayClassInstances.Count; i++)
            {
                GetDisplayClassInstances(displayClassTypes, displayClassInstances, displayClassInstances[i]);
            }
        }
 
        private static void GetDisplayClassInstances(
            HashSet<TypeSymbol> displayClassTypes,
            ArrayBuilder<DisplayClassInstanceAndFields> displayClassInstances,
            DisplayClassInstanceAndFields instance)
        {
            // Display class instance. The display class fields are variables.
            foreach (var member in instance.Type.GetMembers())
            {
                if (member.Kind != SymbolKind.Field)
                {
                    continue;
                }
 
                var field = (FieldSymbol)member;
                var fieldType = field.Type;
                var fieldName = field.Name;
                TryParseGeneratedName(fieldName, out var fieldKind, out var part);
 
                switch (fieldKind)
                {
                    case GeneratedNameKind.DisplayClassLocalOrField:
                    case GeneratedNameKind.TransparentIdentifier:
                        break;
                    case GeneratedNameKind.AnonymousTypeField:
                        RoslynDebug.AssertNotNull(part);
                        if (GeneratedNameParser.GetKind(part) != GeneratedNameKind.TransparentIdentifier)
                        {
                            continue;
                        }
                        break;
                    case GeneratedNameKind.ThisProxyField:
                        if (GeneratedNameParser.GetKind(fieldType.Name) != GeneratedNameKind.LambdaDisplayClass)
                        {
                            continue;
                        }
                        // Async lambda case.
                        break;
                    default:
                        continue;
                }
 
                Debug.Assert(!field.IsStatic);
 
                // A hoisted local that is itself a display class instance.
                if (displayClassTypes.Add(fieldType))
                {
                    var other = instance.FromField(field);
                    displayClassInstances.Add(other);
                }
            }
        }
 
        /// <summary>
        /// Returns true if the parameter is a synthesized parameter representing
        /// a display class instance (used to pass hoisted symbols to local functions).
        /// </summary>
        private static bool IsDisplayClassParameter(ParameterSymbol parameter)
        {
            var type = parameter.Type;
            var result = type.Kind == SymbolKind.NamedType && IsDisplayClassType((NamedTypeSymbol)type);
            Debug.Assert(!result || parameter.MetadataName == "");
            return result;
        }
 
        private static void GetDisplayClassVariables(
            ArrayBuilder<string> displayClassVariableNamesInOrderBuilder,
            Dictionary<string, DisplayClassVariable> displayClassVariablesBuilder,
            HashSet<string> parameterNames,
            ImmutableSortedSet<int> inScopeHoistedLocalSlots,
            DisplayClassInstanceAndFields instance)
        {
            // Display class instance. The display class fields are variables.
            foreach (var member in instance.Type.GetMembers())
            {
                if (member.Kind != SymbolKind.Field)
                {
                    continue;
                }
 
                var field = (FieldSymbol)member;
                var fieldName = field.Name;
 
REPARSE:
 
                DisplayClassVariableKind variableKind;
                string variableName;
                TryParseGeneratedName(fieldName, out var fieldKind, out var part);
 
                switch (fieldKind)
                {
                    case GeneratedNameKind.AnonymousTypeField:
                        RoslynDebug.AssertNotNull(part);
                        Debug.Assert(fieldName == field.Name); // This only happens once.
 
                        fieldName = part;
                        goto REPARSE;
 
                    case GeneratedNameKind.TransparentIdentifier:
                        // A transparent identifier (field) in an anonymous type synthesized for a transparent identifier.
                        Debug.Assert(!field.IsStatic);
                        continue;
 
                    case GeneratedNameKind.DisplayClassLocalOrField:
                        // A local that is itself a display class instance.
                        Debug.Assert(!field.IsStatic);
                        continue;
 
                    case GeneratedNameKind.HoistedLocalField:
                        RoslynDebug.AssertNotNull(part);
 
                        // Filter out hoisted locals that are known to be out-of-scope at the current IL offset.
                        // Hoisted locals with invalid indices will be included since more information is better
                        // than less in error scenarios.
                        if (GeneratedNameParser.TryParseSlotIndex(fieldName, out int slotIndex) &&
                            !inScopeHoistedLocalSlots.Contains(slotIndex))
                        {
                            continue;
                        }
 
                        variableName = part;
                        variableKind = DisplayClassVariableKind.Local;
                        Debug.Assert(!field.IsStatic);
                        break;
 
                    case GeneratedNameKind.ThisProxyField:
                        // A reference to "this".
                        variableName = ""; // Should not be referenced by name.
                        variableKind = DisplayClassVariableKind.This;
                        Debug.Assert(!field.IsStatic);
                        break;
 
                    case GeneratedNameKind.None:
                        // A reference to a parameter or local.
                        variableName = fieldName;
                        variableKind = parameterNames.Contains(variableName) ? DisplayClassVariableKind.Parameter : DisplayClassVariableKind.Local;
                        Debug.Assert(!field.IsStatic);
                        break;
 
                    default:
                        continue;
                }
 
                if (displayClassVariablesBuilder.ContainsKey(variableName))
                {
                    // Only expecting duplicates for async state machine
                    // fields (that should be at the top-level).
                    Debug.Assert(displayClassVariablesBuilder[variableName].DisplayClassFields.Count() == 1);
 
                    if (!instance.Fields.Any())
                    {
                        if (variableKind == DisplayClassVariableKind.Local)
                        {
                            // Prefer parameters over locals.
                            Debug.Assert(instance.Instance is DisplayClassInstanceFromLocal ||
                                         (instance.Instance is DisplayClassInstanceFromParameter && GeneratedNameParser.GetKind(instance.Type.Name) == GeneratedNameKind.LambdaDisplayClass));
                        }
                        else
                        {
                            Debug.Assert(variableKind == DisplayClassVariableKind.Parameter);
                            Debug.Assert(GeneratedNameParser.GetKind(instance.Type.Name) == GeneratedNameKind.StateMachineType);
 
                            if (variableKind == DisplayClassVariableKind.Parameter && GeneratedNameParser.GetKind(instance.Type.Name) == GeneratedNameKind.StateMachineType)
                            {
                                displayClassVariablesBuilder[variableName] = instance.ToVariable(variableName, variableKind, field);
                            }
                        }
                    }
                    else
                    {
                        Debug.Assert(instance.Fields.Count() >= 1); // greater depth
                        Debug.Assert(variableKind == DisplayClassVariableKind.Parameter || variableKind == DisplayClassVariableKind.This);
 
                        if (variableKind == DisplayClassVariableKind.Parameter && GeneratedNameParser.GetKind(instance.Type.Name) == GeneratedNameKind.LambdaDisplayClass)
                        {
                            displayClassVariablesBuilder[variableName] = instance.ToVariable(variableName, variableKind, field);
                        }
                    }
                }
                else if (variableKind != DisplayClassVariableKind.This || GeneratedNameParser.GetKind(instance.Type.ContainingType.Name) != GeneratedNameKind.LambdaDisplayClass)
                {
                    // In async lambdas, the hoisted "this" field in the state machine type will point to the display class instance, if there is one.
                    // In such cases, we want to add the display class "this" to the map instead (or nothing, if it lacks one).
                    displayClassVariableNamesInOrderBuilder.Add(variableName);
                    displayClassVariablesBuilder.Add(variableName, instance.ToVariable(variableName, variableKind, field));
                }
            }
        }
 
        private static void TryParseGeneratedName(string name, out GeneratedNameKind kind, out string? part)
        {
            _ = GeneratedNameParser.TryParseGeneratedName(name, out kind, out int openBracketOffset, out int closeBracketOffset);
            switch (kind)
            {
                case GeneratedNameKind.AnonymousTypeField:
                case GeneratedNameKind.HoistedLocalField:
                    part = name.Substring(openBracketOffset + 1, closeBracketOffset - openBracketOffset - 1);
                    break;
 
                default:
                    part = null;
                    break;
            }
        }
 
        private static bool IsDisplayClassType(TypeSymbol type)
        {
            return type.IsDisplayClassType();
        }
 
        internal static DisplayClassVariable GetThisProxy(ImmutableDictionary<string, DisplayClassVariable> displayClassVariables)
        {
            return displayClassVariables.Values.FirstOrDefault(v => v.Kind == DisplayClassVariableKind.This);
        }
 
        private static NamedTypeSymbol GetNonDisplayClassContainer(NamedTypeSymbol type)
        {
            // 1) Display class and state machine types are always nested within the types
            //    that use them (so that they can access private members of those types).
            // 2) The native compiler used to produce nested display classes for nested lambdas,
            //    so we may have to walk out more than one level.
            while (IsDisplayClassType(type))
            {
                type = type.ContainingType!;
            }
 
            return type;
        }
 
        /// <summary>
        /// Identifies the method in which binding should occur.
        /// </summary>
        /// <param name="candidateSubstitutedSourceMethod">
        /// The symbol of the method that is currently on top of the callstack, with
        /// EE type parameters substituted in place of the original type parameters.
        /// </param>
        /// <param name="sourceMethodMustBeInstance">
        /// True if "this" is available via a display class in the current context.
        /// </param>
        /// <returns>
        /// If <paramref name="candidateSubstitutedSourceMethod"/> is compiler-generated,
        /// then we will attempt to determine which user-defined method caused it to be
        /// generated.  For example, if <paramref name="candidateSubstitutedSourceMethod"/>
        /// is a state machine MoveNext method, then we will try to find the iterator or
        /// async method for which it was generated.  If we are able to find the original
        /// method, then we will substitute in the EE type parameters.  Otherwise, we will
        /// return <paramref name="candidateSubstitutedSourceMethod"/>.
        /// </returns>
        /// <remarks>
        /// In the event that the original method is overloaded, we may not be able to determine
        /// which overload actually corresponds to the state machine.  In particular, we do not
        /// have information about the signature of the original method (i.e. number of parameters,
        /// parameter types and ref-kinds, return type).  However, we conjecture that this
        /// level of uncertainty is acceptable, since parameters are managed by a separate binder
        /// in the synthesized binder chain and we have enough information to check the other method
        /// properties that are used during binding (e.g. static-ness, generic arity, type parameter
        /// constraints).
        /// </remarks>
        internal static MethodSymbol GetSubstitutedSourceMethod(
            MethodSymbol candidateSubstitutedSourceMethod,
            bool sourceMethodMustBeInstance)
        {
            Debug.Assert(candidateSubstitutedSourceMethod.DeclaringCompilation is not null);
 
            var candidateSubstitutedSourceType = candidateSubstitutedSourceMethod.ContainingType;
 
            string? desiredMethodName;
            if (GeneratedNameParser.TryParseSourceMethodNameFromGeneratedName(candidateSubstitutedSourceType.Name, GeneratedNameKind.StateMachineType, out desiredMethodName) ||
                GeneratedNameParser.TryParseSourceMethodNameFromGeneratedName(candidateSubstitutedSourceMethod.Name, GeneratedNameKind.LambdaMethod, out desiredMethodName) ||
                GeneratedNameParser.TryParseSourceMethodNameFromGeneratedName(candidateSubstitutedSourceMethod.Name, GeneratedNameKind.LocalFunction, out desiredMethodName))
            {
                // We could be in the MoveNext method of an async lambda.
                string? tempMethodName;
                if (GeneratedNameParser.TryParseSourceMethodNameFromGeneratedName(desiredMethodName, GeneratedNameKind.LambdaMethod, out tempMethodName) ||
                    GeneratedNameParser.TryParseSourceMethodNameFromGeneratedName(desiredMethodName, GeneratedNameKind.LocalFunction, out tempMethodName))
                {
                    desiredMethodName = tempMethodName;
 
                    var containing = candidateSubstitutedSourceType.ContainingType;
                    RoslynDebug.AssertNotNull(containing);
 
                    if (GeneratedNameParser.GetKind(containing.Name) == GeneratedNameKind.LambdaDisplayClass)
                    {
                        candidateSubstitutedSourceType = containing;
                        sourceMethodMustBeInstance = candidateSubstitutedSourceType.MemberNames.Select(GeneratedNameParser.GetKind).Contains(GeneratedNameKind.ThisProxyField);
                    }
                }
 
                var desiredTypeParameters = candidateSubstitutedSourceType.OriginalDefinition.TypeParameters;
 
                // Type containing the original iterator, async, or lambda-containing method.
                var substitutedSourceType = GetNonDisplayClassContainer(candidateSubstitutedSourceType);
 
                foreach (var candidateMethod in substitutedSourceType.GetMembers().OfType<MethodSymbol>())
                {
                    if (IsViableSourceMethod(candidateMethod, desiredMethodName, desiredTypeParameters, sourceMethodMustBeInstance))
                    {
                        MethodSymbol sourceMethod = new EECompilationContextMethod(candidateSubstitutedSourceMethod.DeclaringCompilation!, candidateMethod.OriginalDefinition);
                        sourceMethod = sourceMethod.AsMember(substitutedSourceType);
 
                        return desiredTypeParameters.Length == 0
                            ? sourceMethod
                            : sourceMethod.Construct(candidateSubstitutedSourceType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics);
                    }
                }
 
                Debug.Fail("Why didn't we find a substituted source method for " + candidateSubstitutedSourceMethod + "?");
            }
 
            return candidateSubstitutedSourceMethod;
        }
 
        private static bool IsViableSourceMethod(
            MethodSymbol candidateMethod,
            string desiredMethodName,
            ImmutableArray<TypeParameterSymbol> desiredTypeParameters,
            bool desiredMethodMustBeInstance)
        {
            return
                !candidateMethod.IsAbstract &&
                !(desiredMethodMustBeInstance && candidateMethod.IsStatic) &&
                candidateMethod.Name == desiredMethodName &&
                HaveSameConstraints(candidateMethod.TypeParameters, desiredTypeParameters);
        }
 
        private static bool HaveSameConstraints(ImmutableArray<TypeParameterSymbol> candidateTypeParameters, ImmutableArray<TypeParameterSymbol> desiredTypeParameters)
        {
            int arity = candidateTypeParameters.Length;
            if (arity != desiredTypeParameters.Length)
            {
                return false;
            }
 
            if (arity == 0)
            {
                return true;
            }
 
            var indexedTypeParameters = IndexedTypeParameterSymbol.Take(arity);
            var candidateTypeMap = new TypeMap(candidateTypeParameters, indexedTypeParameters, allowAlpha: true);
            var desiredTypeMap = new TypeMap(desiredTypeParameters, indexedTypeParameters, allowAlpha: true);
 
            return MemberSignatureComparer.HaveSameConstraints(candidateTypeParameters, candidateTypeMap, desiredTypeParameters, desiredTypeMap);
        }
 
        [DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
        private readonly struct DisplayClassInstanceAndFields
        {
            internal readonly DisplayClassInstance Instance;
            internal readonly ConsList<FieldSymbol> Fields;
 
            internal DisplayClassInstanceAndFields(DisplayClassInstance instance)
                : this(instance, ConsList<FieldSymbol>.Empty)
            {
                Debug.Assert(IsDisplayClassType(instance.Type) ||
                    GeneratedNameParser.GetKind(instance.Type.Name) == GeneratedNameKind.AnonymousType ||
                    instance.Type.GetMembers().OfType<FieldSymbol>().Any(static f => GeneratedNameParser.TryParsePrimaryConstructorParameterFieldName(f.Name, out _)));
            }
 
            private DisplayClassInstanceAndFields(DisplayClassInstance instance, ConsList<FieldSymbol> fields)
            {
                Instance = instance;
                Fields = fields;
            }
 
            internal TypeSymbol Type
                => Fields.Any() ? Fields.Head.Type : Instance.Type;
 
            internal int Depth
                => Fields.Count();
 
            internal DisplayClassInstanceAndFields FromField(FieldSymbol field)
            {
                Debug.Assert(IsDisplayClassType(field.Type) ||
                    GeneratedNameParser.GetKind(field.Type.Name) == GeneratedNameKind.AnonymousType);
                return new DisplayClassInstanceAndFields(Instance, Fields.Prepend(field));
            }
 
            internal DisplayClassVariable ToVariable(string name, DisplayClassVariableKind kind, FieldSymbol field)
            {
                return new DisplayClassVariable(name, kind, Instance, Fields.Prepend(field));
            }
 
            private string GetDebuggerDisplay()
            {
                return Instance.GetDebuggerDisplay(Fields);
            }
        }
    }
}