File: Binder\Binder_Flags.cs
Web Access
Project: src\src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp)
// 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.CSharp.Symbols;
using System.Diagnostics;

namespace Microsoft.CodeAnalysis.CSharp
{
    internal partial class Binder
    {
        /// <summary>
        /// Represents a small change from the enclosing/next binder.
        /// Can specify a BindingLocation and a ContainingMemberOrLambda.
        /// </summary>
        private sealed class BinderWithContainingMemberOrLambda : Binder
        {
            private readonly Symbol _containingMemberOrLambda;

            internal BinderWithContainingMemberOrLambda(Binder next, Symbol containingMemberOrLambda)
                : base(next)
            {
                Debug.Assert(containingMemberOrLambda != null);

                _containingMemberOrLambda = containingMemberOrLambda;
            }

            internal BinderWithContainingMemberOrLambda(Binder next, BinderFlags flags, Symbol containingMemberOrLambda)
                : base(next, flags)
            {
                Debug.Assert(containingMemberOrLambda != null);

                _containingMemberOrLambda = containingMemberOrLambda;
            }

            internal override Symbol ContainingMemberOrLambda
            {
                get { return _containingMemberOrLambda; }
            }
        }

        /// <summary>
        /// Represents a small change from the enclosing/next binder.
        /// Can specify a receiver Expression for containing conditional member access.
        /// </summary>
        private sealed class BinderWithConditionalReceiver : Binder
        {
            private readonly BoundExpression _receiverExpression;

            internal BinderWithConditionalReceiver(Binder next, BoundExpression receiverExpression)
                : base(next)
            {
                Debug.Assert(receiverExpression != null);

                _receiverExpression = receiverExpression;
            }

            internal override BoundExpression ConditionalReceiverExpression
            {
                get { return _receiverExpression; }
            }
        }

        internal Binder WithFlags(BinderFlags flags)
        {
            return this.Flags == flags
                ? this
                : new Binder(this, flags);
        }

        internal Binder WithAdditionalFlags(BinderFlags flags)
        {
            return this.Flags.Includes(flags)
                ? this
                : new Binder(this, this.Flags | flags);
        }

        internal Binder WithContainingMemberOrLambda(Symbol containing)
        {
            Debug.Assert((object)containing != null);
            return new BinderWithContainingMemberOrLambda(this, containing);
        }

        /// <remarks>
        /// It seems to be common to do both of these things at once, so provide a way to do so
        /// without adding two links to the binder chain.
        /// </remarks>
        internal Binder WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags flags, Symbol containing)
        {
            Debug.Assert((object)containing != null);
            return new BinderWithContainingMemberOrLambda(this, this.Flags | flags, containing);
        }

        internal Binder SetOrClearUnsafeRegionIfNecessary(SyntaxTokenList modifiers, bool isIteratorBody = false, bool isFieldDeclaration = false)
        {
            // In C# 13 and above, iterator bodies define a safe context even when nested in an unsafe context.
            // In C# 12 and below, we keep the (spec violating) behavior that iterator bodies inherit the safe/unsafe context
            // from their containing scope. Since there are errors for unsafe constructs directly in iterators,
            // this inherited unsafe context can be observed only in nested non-iterator local functions.
            var withoutUnsafe = isIteratorBody && this.Compilation.IsFeatureEnabled(MessageID.IDS_FeatureRefUnsafeInIteratorAsync);

            if (this.Flags.Includes(BinderFlags.UnsafeRegion))
            {
                return withoutUnsafe
                    ? new Binder(this, this.Flags & ~BinderFlags.UnsafeRegion)
                    : this;
            }

            // Under the updated memory safety rules, the `unsafe` modifier on a type or member declaration
            // does not introduce an unsafe context (no interior meaning), except for field declarations.
            // `unsafe { }` blocks also introduce an unsafe context.
            return !withoutUnsafe && modifiers.Any(SyntaxKind.UnsafeKeyword) && (isFieldDeclaration || !this.Compilation.SourceModule.UseUpdatedMemorySafetyRules)
                ? new Binder(this, this.Flags | BinderFlags.UnsafeRegion)
                : this;
        }

        internal Binder WithCheckedOrUncheckedRegion(bool @checked)
        {
            Debug.Assert(!this.Flags.Includes(BinderFlags.UncheckedRegion | BinderFlags.CheckedRegion));

            BinderFlags added = @checked ? BinderFlags.CheckedRegion : BinderFlags.UncheckedRegion;
            BinderFlags removed = @checked ? BinderFlags.UncheckedRegion : BinderFlags.CheckedRegion;

            return this.Flags.Includes(added)
                ? this
                : new Binder(this, (this.Flags & ~removed) | added);
        }
    }
}