File: Symbols\Source\SourceFieldLikeEventSymbol.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.
 
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
    /// <summary>
    /// This class represents an event declared in source without explicit accessors.
    /// It implicitly has thread safe accessors and an associated field (of the same
    /// name), unless it does not have an initializer and is either extern or inside
    /// an interface, in which case it only has accessors.
    /// </summary>
    internal sealed class SourceFieldLikeEventSymbol : SourceEventSymbol
    {
        private readonly string _name;
        private readonly TypeWithAnnotations _type;
        private readonly SynthesizedEventAccessorSymbol _addMethod;
        private readonly SynthesizedEventAccessorSymbol _removeMethod;
 
        internal SourceFieldLikeEventSymbol(SourceMemberContainerTypeSymbol containingType, Binder binder, SyntaxTokenList modifiers, VariableDeclaratorSyntax declaratorSyntax, BindingDiagnosticBag diagnostics)
            : base(containingType, declaratorSyntax, modifiers, isFieldLike: true, interfaceSpecifierSyntaxOpt: null,
                   nameTokenSyntax: declaratorSyntax.Identifier, diagnostics: diagnostics)
        {
            Debug.Assert(declaratorSyntax.Parent is object);
 
            _name = declaratorSyntax.Identifier.ValueText;
 
            var declaratorDiagnostics = BindingDiagnosticBag.GetInstance();
            var declarationSyntax = (VariableDeclarationSyntax)declaratorSyntax.Parent;
            _type = BindEventType(binder, declarationSyntax.Type, declaratorDiagnostics);
 
            // The runtime will not treat the accessors of this event as overrides or implementations
            // of those of another event unless both the signatures and the custom modifiers match.
            // Hence, in the case of overrides and *explicit* implementations (not possible for field-like
            // events), we need to copy the custom modifiers that are in the signatures of the 
            // overridden/implemented event accessors. (From source, we know that there can only be one 
            // overridden/implemented event, so there are no conflicts.)  This is unnecessary for implicit 
            // implementations because, if the custom modifiers don't match, we'll insert bridge methods 
            // for the accessors (explicit implementations that delegate to the implicit implementations) 
            // with the correct custom modifiers (see SourceMemberContainerTypeSymbol.SynthesizeInterfaceMemberImplementation).
 
            // If this event is an override, we may need to copy custom modifiers from
            // the overridden event (so that the runtime will recognize it as an override).
            // We check for this case here, while we can still modify the parameters and
            // return type without losing the appearance of immutability.
            if (this.IsOverride)
            {
                EventSymbol? overriddenEvent = this.OverriddenEvent;
                if ((object?)overriddenEvent != null)
                {
                    CopyEventCustomModifiers(overriddenEvent, ref _type, ContainingAssembly);
                }
            }
 
            bool hasInitializer = declaratorSyntax.Initializer != null;
            bool inInterfaceType = containingType.IsInterfaceType();
 
            if (hasInitializer)
            {
                if (inInterfaceType && !this.IsStatic)
                {
                    diagnostics.Add(ErrorCode.ERR_InterfaceEventInitializer, this.GetFirstLocation(), this);
                }
                else if (this.IsAbstract)
                {
                    diagnostics.Add(ErrorCode.ERR_AbstractEventInitializer, this.GetFirstLocation(), this);
                }
                else if (this.IsExtern)
                {
                    diagnostics.Add(ErrorCode.ERR_ExternEventInitializer, this.GetFirstLocation(), this);
                }
            }
 
            // NOTE: if there's an initializer in source, we'd better create a backing field, regardless of
            // whether or not the initializer is legal.
            if (hasInitializer || !(this.IsExtern || this.IsAbstract))
            {
                AssociatedEventField = MakeAssociatedField(declaratorSyntax);
                // Don't initialize this.type - we'll just use the type of the field (which is lazy and handles var)
            }
 
            if (!IsStatic && ContainingType.IsReadOnly)
            {
                diagnostics.Add(ErrorCode.ERR_FieldlikeEventsInRoStruct, this.GetFirstLocation());
            }
 
            if (inInterfaceType)
            {
                if ((IsAbstract || IsVirtual) && IsStatic)
                {
                    if (!ContainingAssembly.RuntimeSupportsStaticAbstractMembersInInterfaces)
                    {
                        diagnostics.Add(ErrorCode.ERR_RuntimeDoesNotSupportStaticAbstractMembersInInterfaces, this.GetFirstLocation());
                    }
                }
                else if (this.IsExtern || this.IsStatic)
                {
                    if (!ContainingAssembly.RuntimeSupportsDefaultInterfaceImplementation)
                    {
                        diagnostics.Add(ErrorCode.ERR_RuntimeDoesNotSupportDefaultInterfaceImplementation, this.GetFirstLocation());
                    }
                }
                else if (!this.IsAbstract)
                {
                    diagnostics.Add(ErrorCode.ERR_EventNeedsBothAccessors, this.GetFirstLocation(), this);
                }
            }
 
            _addMethod = new SynthesizedEventAccessorSymbol(this, isAdder: true, isExpressionBodied: false);
            _removeMethod = new SynthesizedEventAccessorSymbol(this, isAdder: false, isExpressionBodied: false);
 
            if (declarationSyntax.Variables[0] == declaratorSyntax)
            {
                // Don't report these diagnostics for every declarator in this declaration.
                diagnostics.AddRange(declaratorDiagnostics);
            }
 
            declaratorDiagnostics.Free();
        }
 
        /// <summary>
        /// Backing field for field-like event. Will be null if the event
        /// has no initializer and is either extern or inside an interface.
        /// </summary>
        internal override FieldSymbol? AssociatedField => AssociatedEventField;
 
        internal SourceEventFieldSymbol? AssociatedEventField { get; }
 
        public override string Name
        {
            get { return _name; }
        }
 
        public override TypeWithAnnotations TypeWithAnnotations
        {
            get { return _type; }
        }
 
        public override MethodSymbol AddMethod
        {
            get { return _addMethod; }
        }
 
        public override MethodSymbol RemoveMethod
        {
            get { return _removeMethod; }
        }
 
        internal override bool IsExplicitInterfaceImplementation
        {
            get { return false; }
        }
 
        protected override AttributeLocation AllowedAttributeLocations
        {
            get
            {
                return (object?)AssociatedEventField != null ?
                    AttributeLocation.Event | AttributeLocation.Method | AttributeLocation.Field :
                    AttributeLocation.Event | AttributeLocation.Method;
            }
        }
 
        public override ImmutableArray<EventSymbol> ExplicitInterfaceImplementations
        {
            get { return ImmutableArray<EventSymbol>.Empty; }
        }
 
        private SourceEventFieldSymbol MakeAssociatedField(VariableDeclaratorSyntax declaratorSyntax)
        {
            var field = new SourceEventFieldSymbol(this, declaratorSyntax, BindingDiagnosticBag.Discarded);
 
            Debug.Assert(field.Name == _name);
            return field;
        }
 
        internal override void ForceComplete(SourceLocation? locationOpt, Predicate<Symbol>? filter, CancellationToken cancellationToken)
        {
            if ((object?)this.AssociatedField != null)
            {
                this.AssociatedField.ForceComplete(locationOpt, filter, cancellationToken);
            }
 
            base.ForceComplete(locationOpt, filter, cancellationToken);
        }
    }
}