File: Compilation\InitializerSemanticModel.cs
Web Access
Project: src\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.
 
#nullable disable
 
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    /// <summary>
    /// A binding for a field initializer, property initializer, constructor
    /// initializer, or a parameter default value.
    /// Represents the result of binding a value expression rather than a
    /// block (for that, use a <see cref="MethodBodySemanticModel"/>).
    /// </summary>
    internal sealed class InitializerSemanticModel : MemberSemanticModel
    {
        // create a SemanticModel for:
        // (a) A true field initializer (field = value) of a named type (incl. Enums) OR
        // (b) A parameter default value
        internal InitializerSemanticModel(CSharpSyntaxNode syntax,
                                     Symbol symbol,
                                     Binder rootBinder,
                                     PublicSemanticModel containingPublicSemanticModel,
                                     ImmutableDictionary<Symbol, Symbol> parentRemappedSymbolsOpt = null) :
            base(syntax, symbol, rootBinder, containingPublicSemanticModel, parentRemappedSymbolsOpt)
        {
            Debug.Assert(!(syntax is ConstructorInitializerSyntax || syntax is PrimaryConstructorBaseTypeSyntax));
        }
 
        /// <summary>
        /// Creates a SemanticModel for a true field initializer (field = value) of a named type (incl. Enums).
        /// </summary>
        internal static InitializerSemanticModel Create(SyntaxTreeSemanticModel containingSemanticModel, CSharpSyntaxNode syntax, FieldSymbol fieldSymbol, Binder rootBinder)
        {
            Debug.Assert(containingSemanticModel != null);
            Debug.Assert(syntax.IsKind(SyntaxKind.VariableDeclarator) || syntax.IsKind(SyntaxKind.EnumMemberDeclaration));
            return new InitializerSemanticModel(syntax, fieldSymbol, rootBinder, containingSemanticModel);
        }
 
        /// <summary>
        /// Creates a SemanticModel for an autoprop initializer of a named type
        /// </summary>
        internal static InitializerSemanticModel Create(SyntaxTreeSemanticModel containingSemanticModel, CSharpSyntaxNode syntax, PropertySymbol propertySymbol, Binder rootBinder)
        {
            Debug.Assert(containingSemanticModel != null);
            Debug.Assert(syntax.IsKind(SyntaxKind.PropertyDeclaration));
            return new InitializerSemanticModel(syntax, propertySymbol, rootBinder, containingSemanticModel);
        }
 
        /// <summary>
        /// Creates a SemanticModel for a parameter default value.
        /// </summary>
        internal static InitializerSemanticModel Create(PublicSemanticModel containingSemanticModel, ParameterSyntax syntax, ParameterSymbol parameterSymbol, Binder rootBinder, ImmutableDictionary<Symbol, Symbol> parentRemappedSymbolsOpt)
        {
            Debug.Assert(containingSemanticModel != null);
            return new InitializerSemanticModel(syntax, parameterSymbol, rootBinder, containingSemanticModel, parentRemappedSymbolsOpt: parentRemappedSymbolsOpt);
        }
 
        /// <summary>
        /// Creates a speculative SemanticModel for an initializer node (field initializer, constructor initializer, or parameter default value)
        /// that did not appear in the original source code.
        /// </summary>
        internal static SpeculativeSemanticModelWithMemberModel CreateSpeculative(SyntaxTreeSemanticModel parentSemanticModel, Symbol owner, EqualsValueClauseSyntax syntax, Binder rootBinder, ImmutableDictionary<Symbol, Symbol> parentRemappedSymbolsOpt, int position)
        {
            return new SpeculativeSemanticModelWithMemberModel(parentSemanticModel, position, owner, syntax, rootBinder, parentRemappedSymbolsOpt);
        }
 
        protected internal override CSharpSyntaxNode GetBindableSyntaxNode(CSharpSyntaxNode node)
        {
            return IsBindableInitializer(node) ? node : base.GetBindableSyntaxNode(node);
        }
 
        internal override BoundNode GetBoundRoot()
        {
            CSharpSyntaxNode rootSyntax = this.Root;
            switch (rootSyntax.Kind())
            {
                case SyntaxKind.VariableDeclarator:
                    rootSyntax = ((VariableDeclaratorSyntax)rootSyntax).Initializer;
                    break;
 
                case SyntaxKind.Parameter:
                    rootSyntax = ((ParameterSyntax)rootSyntax).Default;
                    break;
 
                case SyntaxKind.EqualsValueClause:
                    rootSyntax = ((EqualsValueClauseSyntax)rootSyntax);
                    break;
 
                case SyntaxKind.EnumMemberDeclaration:
                    rootSyntax = ((EnumMemberDeclarationSyntax)rootSyntax).EqualsValue;
                    break;
 
                case SyntaxKind.PropertyDeclaration:
                    rootSyntax = ((PropertyDeclarationSyntax)rootSyntax).Initializer;
                    break;
 
                default:
                    throw ExceptionUtilities.UnexpectedValue(rootSyntax.Kind());
            }
 
            return GetUpperBoundNode(GetBindableSyntaxNode(rootSyntax));
        }
 
        internal override BoundNode Bind(Binder binder, CSharpSyntaxNode node, BindingDiagnosticBag diagnostics)
        {
            EqualsValueClauseSyntax equalsValue = null;
 
            switch (node.Kind())
            {
                case SyntaxKind.EqualsValueClause:
                    equalsValue = (EqualsValueClauseSyntax)node;
                    break;
 
                case SyntaxKind.VariableDeclarator:
                    equalsValue = ((VariableDeclaratorSyntax)node).Initializer;
                    break;
 
                case SyntaxKind.PropertyDeclaration:
                    equalsValue = ((PropertyDeclarationSyntax)node).Initializer;
                    break;
 
                case SyntaxKind.Parameter:
                    equalsValue = ((ParameterSyntax)node).Default;
                    break;
 
                case SyntaxKind.EnumMemberDeclaration:
                    equalsValue = ((EnumMemberDeclarationSyntax)node).EqualsValue;
                    break;
            }
 
            if (equalsValue != null)
            {
                return BindEqualsValue(binder, equalsValue, diagnostics);
            }
 
            return base.Bind(binder, node, diagnostics);
        }
 
        private BoundEqualsValue BindEqualsValue(Binder binder, EqualsValueClauseSyntax equalsValue, BindingDiagnosticBag diagnostics)
        {
            switch (this.MemberSymbol.Kind)
            {
                case SymbolKind.Field:
                    {
                        var field = (FieldSymbol)this.MemberSymbol;
                        var enumField = field as SourceEnumConstantSymbol;
                        if ((object)enumField != null)
                        {
                            return binder.BindEnumConstantInitializer(enumField, equalsValue, diagnostics);
                        }
                        else
                        {
                            return binder.BindFieldInitializer(field, equalsValue, diagnostics);
                        }
                    }
 
                case SymbolKind.Property:
                    {
                        var property = (SourcePropertySymbol)this.MemberSymbol;
                        BoundFieldEqualsValue result = binder.BindFieldInitializer(property.BackingField, equalsValue, diagnostics);
                        return new BoundPropertyEqualsValue(result.Syntax, property, result.Locals, result.Value);
                    }
 
                case SymbolKind.Parameter:
                    {
                        var parameter = (ParameterSymbol)this.MemberSymbol;
                        return binder.BindParameterDefaultValue(
                            equalsValue,
                            parameter,
                            diagnostics,
                            out _);
                    }
 
                default:
                    throw ExceptionUtilities.UnexpectedValue(this.MemberSymbol.Kind);
            }
        }
 
        private bool IsBindableInitializer(CSharpSyntaxNode node)
        {
            // If we are being asked to bind the equals clause (the "=1" part of "double x=1,y=2;"),
            // that's our root and we know how to bind that thing even if it is not an 
            // expression or a statement.
 
            switch (node.Kind())
            {
                case SyntaxKind.EqualsValueClause:
                    return this.Root == node ||     /*enum or parameter initializer*/
                           this.Root == node.Parent /*field initializer*/;
 
                default:
                    return false;
            }
        }
 
        internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out PublicSemanticModel speculativeModel)
        {
            var binder = this.GetEnclosingBinder(position);
            if (binder == null)
            {
                speculativeModel = null;
                return false;
            }
 
            binder = new ExecutableCodeBinder(initializer, binder.ContainingMemberOrLambda, binder);
            speculativeModel = CreateSpeculative(parentModel, this.MemberSymbol, initializer, binder, GetRemappedSymbols(), position);
            return true;
        }
 
        internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out PublicSemanticModel speculativeModel)
        {
            speculativeModel = null;
            return false;
        }
 
        internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out PublicSemanticModel speculativeModel)
        {
            speculativeModel = null;
            return false;
        }
 
        internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ArrowExpressionClauseSyntax expressionBody, out PublicSemanticModel speculativeModel)
        {
            speculativeModel = null;
            return false;
        }
 
        internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, StatementSyntax statement, out PublicSemanticModel speculativeModel)
        {
            speculativeModel = null;
            return false;
        }
 
        internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, BaseMethodDeclarationSyntax method, out PublicSemanticModel speculativeModel)
        {
            speculativeModel = null;
            return false;
        }
 
        internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, AccessorDeclarationSyntax accessor, out PublicSemanticModel speculativeModel)
        {
            speculativeModel = null;
            return false;
        }
 
        protected override BoundNode RewriteNullableBoundNodesWithSnapshots(
            BoundNode boundRoot,
            Binder binder,
            DiagnosticBag diagnostics,
            bool createSnapshots,
            out NullableWalker.SnapshotManager snapshotManager,
            ref ImmutableDictionary<Symbol, Symbol> remappedSymbols)
        {
            // https://github.com/dotnet/roslyn/issues/46424
            // Bind and analyze preceding field initializers in order to give an accurate initial nullable state.
            return NullableWalker.AnalyzeAndRewrite(Compilation, MemberSymbol, boundRoot, binder, initialState: null, diagnostics, createSnapshots, out snapshotManager, ref remappedSymbols);
        }
 
        protected override void AnalyzeBoundNodeNullability(BoundNode boundRoot, Binder binder, DiagnosticBag diagnostics, bool createSnapshots)
        {
            NullableWalker.AnalyzeWithoutRewrite(Compilation, MemberSymbol, boundRoot, binder, diagnostics, createSnapshots);
        }
 
#nullable enable
 
        protected override bool IsNullableAnalysisEnabledCore()
        {
            switch (MemberSymbol.Kind)
            {
                case SymbolKind.Field:
                case SymbolKind.Property:
                    Debug.Assert(MemberSymbol.ContainingType is SourceMemberContainerTypeSymbol);
                    return MemberSymbol.ContainingType is SourceMemberContainerTypeSymbol type &&
                        type.IsNullableEnabledForConstructorsAndInitializers(useStatic: MemberSymbol.IsStatic);
                case SymbolKind.Parameter:
                    return SourceComplexParameterSymbolBase.GetDefaultValueSyntaxForIsNullableAnalysisEnabled(Root as ParameterSyntax) is { } value &&
                        Compilation.IsNullableAnalysisEnabledIn(value);
                default:
                    throw ExceptionUtilities.UnexpectedValue(MemberSymbol.Kind);
            }
        }
    }
}