// 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.

#nullable disable

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.ExpressionEvaluator;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator
{
    internal delegate BoundExpression GenerateThisReference(SyntaxNode syntax);

    internal delegate BoundStatement GenerateMethodBody(
        EEMethodSymbol method,
        DiagnosticBag diagnostics,
        out ImmutableArray<LocalSymbol> declaredLocals,
        out ResultProperties properties);

    /// <summary>
    /// Synthesized expression evaluation method.
    /// </summary>
    internal sealed class EEMethodSymbol : MethodSymbol
    {
        // We only create a single EE method (per EE type) that represents an arbitrary expression,
        // whose lowering may produce synthesized members (lambdas, dynamic sites, etc).
        // We may thus assume that the method ordinal is always 0.
        //
        // Consider making the implementation more flexible in order to avoid this assumption.
        // In future we might need to compile multiple expression and then we'll need to assign 
        // a unique method ordinal to each of them to avoid duplicate synthesized member names.
        private const int _methodOrdinal = 0;

        internal readonly TypeMap TypeMap;
        internal readonly MethodSymbol SubstitutedSourceMethod;
        internal readonly ImmutableArray<LocalSymbol> Locals;

        /// <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>
        internal 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>
        internal readonly ImmutableArray<LocalSymbol> LocalsForBindingInside;

        private readonly EENamedTypeSymbol _container;
        private readonly string _name;
        private readonly ImmutableArray<Location> _locations;
        private readonly ImmutableArray<TypeParameterSymbol> _typeParameters;
        private readonly ImmutableArray<ParameterSymbol> _parameters;
        private readonly ParameterSymbol _thisParameter;
        private readonly ImmutableDictionary<string, DisplayClassVariable> _displayClassVariables;

        /// <summary>
        /// Invoked at most once to generate the method body.
        /// (If the compilation has no errors, it will be invoked
        /// exactly once, otherwise it may be skipped.)
        /// </summary>
        private readonly GenerateMethodBody _generateMethodBody;
        private TypeWithAnnotations _lazyReturnType;
        private ResultProperties _lazyResultProperties;

        // NOTE: This is only used for asserts, so it could be conditional on DEBUG.
        private readonly ImmutableArray<TypeParameterSymbol> _allTypeParameters;

        internal EEMethodSymbol(
            EENamedTypeSymbol container,
            string name,
            Location location,
            MethodSymbol sourceMethod,
            ImmutableArray<LocalSymbol> sourceLocals,
            ImmutableArray<LocalSymbol> sourceLocalsForBindingOutside,
            ImmutableArray<LocalSymbol> sourceLocalsForBindingInside,
            ImmutableDictionary<string, DisplayClassVariable> sourceDisplayClassVariables,
            GenerateMethodBody generateMethodBody)
        {
            Debug.Assert(sourceMethod.IsDefinition);
            Debug.Assert(TypeSymbol.Equals((TypeSymbol)sourceMethod.ContainingSymbol, container.SubstitutedSourceType.OriginalDefinition, TypeCompareKind.ConsiderEverything2));
            Debug.Assert(sourceLocals.All(l => l.ContainingSymbol == sourceMethod));

            _container = container;
            _name = name;
            _locations = ImmutableArray.Create(location);

            // What we want is to map all original type parameters to the corresponding new type parameters
            // (since the old ones have the wrong owners).  Unfortunately, we have a circular dependency:
            //   1) Each new type parameter requires the entire map in order to be able to construct its constraint list.
            //   2) The map cannot be constructed until all new type parameters exist.
            // Our solution is to pass each new type parameter a lazy reference to the type map.  We then 
            // initialize the map as soon as the new type parameters are available - and before they are 
            // handed out - so that there is never a period where they can require the type map and find
            // it uninitialized.

            var sourceMethodTypeParameters = sourceMethod.TypeParameters;
            var allSourceTypeParameters = container.SourceTypeParameters.Concat(sourceMethodTypeParameters);

            sourceMethod = new EECompilationContextMethod(DeclaringCompilation, sourceMethod);

            sourceMethodTypeParameters = sourceMethod.TypeParameters;
            allSourceTypeParameters = allSourceTypeParameters.Concat(sourceMethodTypeParameters);

            var getTypeMap = new Func<TypeMap>(() => this.TypeMap);
            _typeParameters = sourceMethodTypeParameters.SelectAsArray(
                (tp, i, arg) => (TypeParameterSymbol)new EETypeParameterSymbol(this, tp, i, getTypeMap),
                (object)null);
            _allTypeParameters = container.TypeParameters.Concat(_typeParameters).Concat(_typeParameters);
            this.TypeMap = new TypeMap(allSourceTypeParameters, _allTypeParameters, allowAlpha: true);

            EENamedTypeSymbol.VerifyTypeParameters(this, _typeParameters);

            var substitutedSourceType = container.SubstitutedSourceType;
            this.SubstitutedSourceMethod = sourceMethod.AsMember(substitutedSourceType);
            if (sourceMethod.Arity > 0)
            {
                this.SubstitutedSourceMethod = this.SubstitutedSourceMethod.Construct(_typeParameters.As<TypeSymbol>());
            }
            TypeParameterChecker.Check(this.SubstitutedSourceMethod, _allTypeParameters);

            // Create a map from original parameter to target parameter.
            var parameterBuilder = ArrayBuilder<ParameterSymbol>.GetInstance();

            var substitutedSourceThisParameter = this.SubstitutedSourceMethod.ThisParameter;
            var substitutedSourceHasThisParameter = (object)substitutedSourceThisParameter != null;
            if (substitutedSourceHasThisParameter)
            {
                _thisParameter = MakeParameterSymbol(0, GeneratedNames.ThisProxyFieldName(), substitutedSourceThisParameter);
                Debug.Assert(TypeSymbol.Equals(_thisParameter.Type, this.SubstitutedSourceMethod.ContainingType, TypeCompareKind.ConsiderEverything2));
                parameterBuilder.Add(_thisParameter);
            }

            var ordinalOffset = (substitutedSourceHasThisParameter ? 1 : 0);
            foreach (var substitutedSourceParameter in this.SubstitutedSourceMethod.Parameters)
            {
                var ordinal = substitutedSourceParameter.Ordinal + ordinalOffset;
                Debug.Assert(ordinal == parameterBuilder.Count);
                var parameter = MakeParameterSymbol(ordinal, substitutedSourceParameter.Name, substitutedSourceParameter);
                parameterBuilder.Add(parameter);
            }

            _parameters = parameterBuilder.ToImmutableAndFree();

            var localsBuilder = ArrayBuilder<LocalSymbol>.GetInstance();
            var localsMap = PooledDictionary<LocalSymbol, LocalSymbol>.GetInstance();
            foreach (var sourceLocal in sourceLocals)
            {
                var local = sourceLocal.ToOtherMethod(this, this.TypeMap);
                localsMap.Add(sourceLocal, local);
                localsBuilder.Add(local);
            }
            this.Locals = localsBuilder.ToImmutableAndFree();

            this.LocalsForBindingInside = remapLocalsForBinding(sourceLocalsForBindingInside, localsMap);
            this.LocalsForBindingOutside = remapLocalsForBinding(sourceLocalsForBindingOutside, localsMap);

            // Create a map from variable name to display class field.
            var displayClassVariables = PooledDictionary<string, DisplayClassVariable>.GetInstance();
            foreach (var pair in sourceDisplayClassVariables)
            {
                var variable = pair.Value;
                var oldDisplayClassInstance = variable.DisplayClassInstance;

                // Note: we don't call ToOtherMethod in the local case because doing so would produce
                // a new LocalSymbol that would not be ReferenceEquals to the one in this.LocalsForBinding.
                var oldDisplayClassInstanceFromLocal = oldDisplayClassInstance as DisplayClassInstanceFromLocal;
                var newDisplayClassInstance = (oldDisplayClassInstanceFromLocal == null)
                    ? oldDisplayClassInstance.ToOtherMethod(this, this.TypeMap)
                    : new DisplayClassInstanceFromLocal((EELocalSymbol)localsMap[oldDisplayClassInstanceFromLocal.Local]);

                variable = variable.SubstituteFields(newDisplayClassInstance, this.TypeMap);
                displayClassVariables.Add(pair.Key, variable);
            }

            _displayClassVariables = displayClassVariables.ToImmutableDictionary();
            displayClassVariables.Free();
            localsMap.Free();

            _generateMethodBody = generateMethodBody;

            ImmutableArray<LocalSymbol> remapLocalsForBinding(
                ImmutableArray<LocalSymbol> sourceLocalsForBinding,
                Dictionary<LocalSymbol, LocalSymbol> localsMap)
            {
                var localsBuilder = ArrayBuilder<LocalSymbol>.GetInstance(sourceLocalsForBinding.Length);
                foreach (var sourceLocal in sourceLocalsForBinding)
                {
                    LocalSymbol local;
                    if (!localsMap.TryGetValue(sourceLocal, out local))
                    {
                        local = sourceLocal.ToOtherMethod(this, this.TypeMap);
                        localsMap.Add(sourceLocal, local);
                    }
                    localsBuilder.Add(local);
                }

                return localsBuilder.ToImmutableAndFree();
            }
        }

        private ParameterSymbol MakeParameterSymbol(int ordinal, string name, ParameterSymbol sourceParameter)
        {
            return SynthesizedParameterSymbol.Create(this, sourceParameter.TypeWithAnnotations, ordinal, sourceParameter.RefKind, name, ScopedKind.None, refCustomModifiers: sourceParameter.RefCustomModifiers);
        }

        internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false)
        {
            return false;
        }

        internal override bool IsMetadataVirtual(bool ignoreInterfaceImplementationChanges = false)
        {
            return false;
        }

        internal override bool IsMetadataFinal
        {
            get { return false; }
        }

        public override MethodKind MethodKind
        {
            get { return MethodKind.Ordinary; }
        }

        public override string Name
        {
            get { return _name; }
        }

        public override int Arity
        {
            get { return _typeParameters.Length; }
        }

        public override bool IsExtensionMethod
        {
            get { return false; }
        }

        internal override bool HasSpecialName
        {
            get { return true; }
        }

        internal override System.Reflection.MethodImplAttributes ImplementationAttributes
        {
            get { return default(System.Reflection.MethodImplAttributes); }
        }

        internal override bool HasDeclarativeSecurity
        {
            get { return false; }
        }

        public override DllImportData GetDllImportData()
        {
            return null;
        }

        public override bool AreLocalsZeroed
        {
            get { return true; }
        }

        internal override IEnumerable<Cci.SecurityAttribute> GetSecurityInformation()
        {
            throw ExceptionUtilities.Unreachable();
        }

        internal override MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation
        {
            get { return null; }
        }

        internal override bool RequiresSecurityObject
        {
            get { return false; }
        }

        internal override bool TryGetThisParameter(out ParameterSymbol thisParameter)
        {
            thisParameter = null;
            return true;
        }

        public override bool HidesBaseMethodsByName
        {
            get { return false; }
        }

        public override bool IsVararg
        {
            get { return this.SubstitutedSourceMethod.IsVararg; }
        }

        public override RefKind RefKind
        {
            get { return this.SubstitutedSourceMethod.RefKind; }
        }

        public override bool ReturnsVoid
        {
            get { return this.ReturnType.SpecialType == SpecialType.System_Void; }
        }

        public override bool IsAsync
        {
            get { return false; }
        }

        public override TypeWithAnnotations ReturnTypeWithAnnotations
        {
            get
            {
                if ((object)_lazyReturnType == null)
                {
                    throw new InvalidOperationException();
                }
                return _lazyReturnType;
            }
        }

        public override FlowAnalysisAnnotations ReturnTypeFlowAnalysisAnnotations => FlowAnalysisAnnotations.None;

        public override ImmutableHashSet<string> ReturnNotNullIfParameterNotNull => ImmutableHashSet<string>.Empty;

        public override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None;

        public override ImmutableArray<TypeWithAnnotations> TypeArgumentsWithAnnotations
        {
            get { return GetTypeParametersAsTypeArguments(); }
        }

        public override ImmutableArray<TypeParameterSymbol> TypeParameters
        {
            get { return _typeParameters; }
        }

        public override ImmutableArray<ParameterSymbol> Parameters
        {
            get { return _parameters; }
        }

        public override ImmutableArray<MethodSymbol> ExplicitInterfaceImplementations
        {
            get { return ImmutableArray<MethodSymbol>.Empty; }
        }

        public override ImmutableArray<CustomModifier> RefCustomModifiers
        {
            get { return ImmutableArray<CustomModifier>.Empty; }
        }

        public override Symbol AssociatedSymbol
        {
            get { return null; }
        }

        internal override ImmutableArray<string> GetAppliedConditionalSymbols()
        {
            throw ExceptionUtilities.Unreachable();
        }

        internal override Cci.CallingConvention CallingConvention
        {
            get
            {
                Debug.Assert(this.IsStatic);
                var cc = Cci.CallingConvention.Default;
                if (this.IsVararg)
                {
                    cc |= Cci.CallingConvention.ExtraArguments;
                }
                if (this.IsGenericMethod)
                {
                    cc |= Cci.CallingConvention.Generic;
                }
                return cc;
            }
        }

        internal override bool GenerateDebugInfo
        {
            get { return false; }
        }

        public override Symbol ContainingSymbol
        {
            get { return _container; }
        }

        public override ImmutableArray<Location> Locations
        {
            get { return _locations; }
        }

        public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences
        {
            get { return GetDeclaringSyntaxReferenceHelper<CSharpSyntaxNode>(_locations); }
        }

        public override Accessibility DeclaredAccessibility
        {
            get { return Accessibility.Internal; }
        }

        public override bool IsStatic
        {
            get { return true; }
        }

        public override bool IsVirtual
        {
            get { return false; }
        }

        public override bool IsOverride
        {
            get { return false; }
        }

        public override bool IsAbstract
        {
            get { return false; }
        }

        public override bool IsSealed
        {
            get { return false; }
        }

        public override bool IsExtern
        {
            get { return false; }
        }

        internal override bool IsDeclaredReadOnly => false;

        internal override bool IsInitOnly => false;

        internal override ObsoleteAttributeData ObsoleteAttributeData
        {
            get { throw ExceptionUtilities.Unreachable(); }
        }

        internal sealed override UnmanagedCallersOnlyAttributeData GetUnmanagedCallersOnlyAttributeData(bool forceComplete) => throw ExceptionUtilities.Unreachable();

        internal override bool HasUnscopedRefAttribute => false;

        internal override bool UseUpdatedEscapeRules => false;

        internal ResultProperties ResultProperties
        {
            get { return _lazyResultProperties; }
        }

        internal override void GenerateMethodBody(TypeCompilationState compilationState, BindingDiagnosticBag diagnostics)
        {
            ImmutableArray<LocalSymbol> declaredLocalsArray;
            var body = _generateMethodBody(this, diagnostics.DiagnosticBag, out declaredLocalsArray, out _lazyResultProperties);
            var compilation = compilationState.Compilation;

            _lazyReturnType = TypeWithAnnotations.Create(CalculateReturnType(compilation, body));

            // Can't do this until the return type has been computed.
            TypeParameterChecker.Check(this, _allTypeParameters);

            if (diagnostics.HasAnyErrors())
            {
                return;
            }

            DiagnosticsPass.IssueDiagnostics(compilation, body, diagnostics, this);
            if (diagnostics.HasAnyErrors())
            {
                return;
            }

            // Check for use-site diagnostics (e.g. missing types in the signature).
            UseSiteInfo<AssemblySymbol> useSiteInfo = default;
            this.CalculateUseSiteDiagnostic(ref useSiteInfo);
            if (useSiteInfo.DiagnosticInfo != null && useSiteInfo.DiagnosticInfo.Severity == DiagnosticSeverity.Error)
            {
                diagnostics.Add(useSiteInfo.DiagnosticInfo, this.Locations[0]);
                return;
            }

            try
            {
                var declaredLocals = PooledHashSet<LocalSymbol>.GetInstance();
                try
                {
                    // Rewrite local declaration statement.
                    body = (BoundStatement)LocalDeclarationRewriter.Rewrite(
                        compilation,
                        declaredLocals,
                        body,
                        declaredLocalsArray,
                        diagnostics.DiagnosticBag);

                    // Verify local declaration names.
                    foreach (var local in declaredLocals)
                    {
                        Debug.Assert(local.Locations.Length > 0);
                        var name = local.Name;
                        if (name.StartsWith("$", StringComparison.Ordinal))
                        {
                            diagnostics.Add(ErrorCode.ERR_UnexpectedCharacter, local.Locations[0], name[0]);
                            return;
                        }
                    }

                    // Rewrite references to placeholder "locals".
                    body = (BoundStatement)PlaceholderLocalRewriter.Rewrite(compilation, declaredLocals, body, diagnostics.DiagnosticBag);

                    if (diagnostics.HasAnyErrors())
                    {
                        return;
                    }
                }
                finally
                {
                    declaredLocals.Free();
                }

                var syntax = body.Syntax;
                var statementsBuilder = ArrayBuilder<BoundStatement>.GetInstance();
                statementsBuilder.Add(body);
                // Insert an implicit return statement if necessary.
                if (body.Kind != BoundKind.ReturnStatement)
                {
                    statementsBuilder.Add(new BoundReturnStatement(syntax, RefKind.None, expressionOpt: null, @checked: false));
                }

                var localsSet = PooledHashSet<LocalSymbol>.GetInstance();
                try
                {
                    var localsBuilder = ArrayBuilder<LocalSymbol>.GetInstance();
                    foreach (var local in this.LocalsForBindingOutside)
                    {
                        Debug.Assert(!localsSet.Contains(local));
                        localsBuilder.Add(local);
                        localsSet.Add(local);
                    }

                    foreach (var local in this.LocalsForBindingInside)
                    {
                        Debug.Assert(!localsSet.Contains(local));
                        localsBuilder.Add(local);
                        localsSet.Add(local);
                    }

                    foreach (var local in this.Locals)
                    {
                        if (localsSet.Add(local))
                        {
                            localsBuilder.Add(local);
                        }
                    }

                    body = new BoundBlock(syntax, localsBuilder.ToImmutableAndFree(), statementsBuilder.ToImmutableAndFree()) { WasCompilerGenerated = true };

                    Debug.Assert(!diagnostics.HasAnyErrors());
                    Debug.Assert(!body.HasErrors);

                    body = LocalRewriter.Rewrite(
                        compilation: this.DeclaringCompilation,
                        method: this,
                        methodOrdinal: _methodOrdinal,
                        containingType: _container,
                        statement: body,
                        compilationState: compilationState,
                        previousSubmissionFields: null,
                        allowOmissionOfConditionalCalls: false,
                        instrumentation: MethodInstrumentation.Empty,
                        debugDocumentProvider: null,
                        diagnostics: diagnostics,
                        codeCoverageSpans: out ImmutableArray<SourceSpan> codeCoverageSpans,
                        sawLambdas: out bool sawLambdas,
                        sawLocalFunctions: out bool sawLocalFunctions,
                        sawAwaitInExceptionHandler: out bool sawAwaitInExceptionHandler);

                    Debug.Assert(!sawAwaitInExceptionHandler);
                    Debug.Assert(codeCoverageSpans.IsEmpty);

                    if (body.HasErrors)
                    {
                        return;
                    }

                    // Variables may have been captured by lambdas in the original method
                    // or in the expression, and we need to preserve the existing values of
                    // those variables in the expression. This requires rewriting the variables
                    // in the expression based on the closure classes from both the original
                    // method and the expression, and generating a preamble that copies
                    // values into the expression closure classes.
                    //
                    // Consider the original method:
                    // static void M()
                    // {
                    //     int x, y, z;
                    //     ...
                    //     F(() => x + y);
                    // }
                    // and the expression in the EE: "F(() => x + z)".
                    //
                    // The expression is first rewritten using the closure class and local <1>
                    // from the original method: F(() => <1>.x + z)
                    // Then lambda rewriting introduces a new closure class that includes
                    // the locals <1> and z, and a corresponding local <2>: F(() => <2>.<1>.x + <2>.z)
                    // And a preamble is added to initialize the fields of <2>:
                    //     <2> = new <>c__DisplayClass0();
                    //     <2>.<1> = <1>;
                    //     <2>.z = z;

                    // Rewrite "this" and "base" references to parameter in this method.
                    // Rewrite variables within body to reference existing display classes.
                    body = (BoundStatement)CapturedVariableRewriter.Rewrite(
                        this.GenerateThisReference,
                        compilation.Conversions,
                        _displayClassVariables,
                        body,
                        diagnostics.DiagnosticBag);

                    if (body.HasErrors)
                    {
                        Debug.Assert(false, "Please add a test case capturing whatever caused this assert.");
                        return;
                    }

                    if (diagnostics.HasAnyErrors())
                    {
                        return;
                    }

                    if (sawLambdas || sawLocalFunctions)
                    {
                        var closureDebugInfoBuilder = ArrayBuilder<ClosureDebugInfo>.GetInstance();
                        var lambdaDebugInfoBuilder = ArrayBuilder<LambdaDebugInfo>.GetInstance();

                        body = ClosureConversion.Rewrite(
                            loweredBody: body,
                            thisType: this.SubstitutedSourceMethod.ContainingType,
                            thisParameter: _thisParameter,
                            method: this,
                            methodOrdinal: _methodOrdinal,
                            substitutedSourceMethod: this.SubstitutedSourceMethod.OriginalDefinition,
                            closureDebugInfoBuilder: closureDebugInfoBuilder,
                            lambdaDebugInfoBuilder: lambdaDebugInfoBuilder,
                            slotAllocatorOpt: null,
                            compilationState: compilationState,
                            diagnostics: diagnostics,
                            assignLocals: localsSet);

                        // we don't need this information:
                        closureDebugInfoBuilder.Free();
                        lambdaDebugInfoBuilder.Free();
                    }
                }
                finally
                {
                    localsSet.Free();
                }

                // Insert locals from the original method,
                // followed by any new locals.
                var block = (BoundBlock)body;
                var localBuilder = ArrayBuilder<LocalSymbol>.GetInstance();
                foreach (var local in this.Locals)
                {
                    Debug.Assert(!(local is EELocalSymbol) || (((EELocalSymbol)local).Ordinal == localBuilder.Count));
                    localBuilder.Add(local);
                }
                foreach (var local in block.Locals)
                {
                    if (local is EELocalSymbol oldLocal)
                    {
                        Debug.Assert(localBuilder[oldLocal.Ordinal] == oldLocal);
                        continue;
                    }
                    localBuilder.Add(local);
                }

                body = block.Update(localBuilder.ToImmutableAndFree(), block.LocalFunctions, block.HasUnsafeModifier, instrumentation: null, block.Statements);
                TypeParameterChecker.Check(body, _allTypeParameters);
                compilationState.AddSynthesizedMethod(this, body);
            }
            catch (BoundTreeVisitor.CancelledByStackGuardException ex)
            {
                ex.AddAnError(diagnostics);
            }
        }

        private BoundExpression GenerateThisReference(SyntaxNode syntax)
        {
            var thisProxy = CompilationContext.GetThisProxy(_displayClassVariables);
            if (thisProxy != null)
            {
                return thisProxy.ToBoundExpression(syntax);
            }
            if ((object)_thisParameter != null)
            {
                var typeNameKind = GeneratedNameParser.GetKind(_thisParameter.TypeWithAnnotations.Type.Name);
                if (typeNameKind != GeneratedNameKind.None && typeNameKind != GeneratedNameKind.AnonymousType)
                {
                    Debug.Assert(typeNameKind == GeneratedNameKind.LambdaDisplayClass ||
                        typeNameKind == GeneratedNameKind.StateMachineType,
                        $"Unexpected typeNameKind '{typeNameKind}'");
                    return null;
                }
                return new BoundParameter(syntax, _thisParameter);
            }
            return null;
        }

        private static TypeSymbol CalculateReturnType(CSharpCompilation compilation, BoundStatement bodyOpt)
        {
            if (bodyOpt == null)
            {
                // If the method doesn't do anything, then it doesn't return anything.
                return compilation.GetSpecialType(SpecialType.System_Void);
            }

            switch (bodyOpt.Kind)
            {
                case BoundKind.ReturnStatement:
                    return ((BoundReturnStatement)bodyOpt).ExpressionOpt.Type;
                case BoundKind.ExpressionStatement:
                case BoundKind.LocalDeclaration:
                case BoundKind.MultipleLocalDeclarations:
                    return compilation.GetSpecialType(SpecialType.System_Void);
                default:
                    throw ExceptionUtilities.UnexpectedValue(bodyOpt.Kind);
            }
        }

        internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree)
        {
            return localPosition;
        }

        internal override bool IsNullableAnalysisEnabled() => false;

        protected override bool HasSetsRequiredMembersImpl => throw ExceptionUtilities.Unreachable();
    }
}
