// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
///
/// This class represents an event declared in source with explicit accessors
/// (i.e. not a field-like event).
///
internal sealed class SourceCustomEventSymbol : SourceEventSymbol
{
private readonly TypeSymbol _type;
private readonly string _name;
private readonly SourceCustomEventAccessorSymbol _addMethod;
private readonly SourceCustomEventAccessorSymbol _removeMethod;
private readonly TypeSymbol _explicitInterfaceType;
private readonly ImmutableArray _explicitInterfaceImplementations;
internal SourceCustomEventSymbol(SourceMemberContainerTypeSymbol containingType, Binder binder, EventDeclarationSyntax syntax, DiagnosticBag diagnostics) :
base(containingType, syntax, syntax.Modifiers, syntax.ExplicitInterfaceSpecifier, syntax.Identifier, diagnostics)
{
ExplicitInterfaceSpecifierSyntax interfaceSpecifier = syntax.ExplicitInterfaceSpecifier;
SyntaxToken nameToken = syntax.Identifier;
bool isExplicitInterfaceImplementation = interfaceSpecifier != null;
string aliasQualifierOpt;
_name = ExplicitInterfaceHelpers.GetMemberNameAndInterfaceSymbol(binder, interfaceSpecifier, nameToken.ValueText, diagnostics, out _explicitInterfaceType, out aliasQualifierOpt);
_type = BindEventType(binder, syntax.Type, diagnostics);
var explicitlyImplementedEvent = this.FindExplicitlyImplementedEvent(_explicitInterfaceType, nameToken.ValueText, interfaceSpecifier, diagnostics);
// 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, 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 SourceNamedTypeSymbol.ImplementInterfaceMember).
// Note: we're checking if the syntax indicates explicit implementation rather,
// than if explicitInterfaceType is null because we don't want to look for an
// overridden event if this is supposed to be an explicit implementation.
if (!isExplicitInterfaceImplementation)
{
// 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);
}
}
}
else if ((object)explicitlyImplementedEvent != null)
{
CopyEventCustomModifiers(explicitlyImplementedEvent, ref _type, ContainingAssembly);
}
AccessorDeclarationSyntax addSyntax = null;
AccessorDeclarationSyntax removeSyntax = null;
foreach (AccessorDeclarationSyntax accessor in syntax.AccessorList.Accessors)
{
switch (accessor.Kind())
{
case SyntaxKind.AddAccessorDeclaration:
if (addSyntax == null || addSyntax.Keyword.Span.IsEmpty)
{
addSyntax = accessor;
}
break;
case SyntaxKind.RemoveAccessorDeclaration:
if (removeSyntax == null || removeSyntax.Keyword.Span.IsEmpty)
{
removeSyntax = accessor;
}
break;
}
}
_addMethod = CreateAccessorSymbol(addSyntax, explicitlyImplementedEvent, aliasQualifierOpt, diagnostics);
_removeMethod = CreateAccessorSymbol(removeSyntax, explicitlyImplementedEvent, aliasQualifierOpt, diagnostics);
if (containingType.IsInterfaceType())
{
if (addSyntax == null && removeSyntax == null) //NOTE: AND - different error code produced if one is present
{
// CONSIDER: we're matching dev10, but it would probably be more helpful to give
// an error like ERR_EventPropertyInInterface.
diagnostics.Add(ErrorCode.ERR_EventNeedsBothAccessors, this.Locations[0], this);
}
}
else
{
if (addSyntax == null || removeSyntax == null)
{
diagnostics.Add(ErrorCode.ERR_EventNeedsBothAccessors, this.Locations[0], this);
}
}
_explicitInterfaceImplementations =
(object)explicitlyImplementedEvent == null ?
ImmutableArray.Empty :
ImmutableArray.Create(explicitlyImplementedEvent);
}
public override TypeSymbol Type
{
get { return _type; }
}
public override string Name
{
get { return _name; }
}
public override MethodSymbol AddMethod
{
get { return _addMethod; }
}
public override MethodSymbol RemoveMethod
{
get { return _removeMethod; }
}
protected override AttributeLocation AllowedAttributeLocations
{
get { return AttributeLocation.Event; }
}
private ExplicitInterfaceSpecifierSyntax ExplicitInterfaceSpecifier
{
get { return ((EventDeclarationSyntax)this.CSharpSyntaxNode).ExplicitInterfaceSpecifier; }
}
internal override bool IsExplicitInterfaceImplementation
{
get { return this.ExplicitInterfaceSpecifier != null; }
}
public override ImmutableArray ExplicitInterfaceImplementations
{
get { return _explicitInterfaceImplementations; }
}
internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, DiagnosticBag diagnostics)
{
base.AfterAddingTypeMembersChecks(conversions, diagnostics);
if ((object)_explicitInterfaceType != null)
{
var explicitInterfaceSpecifier = this.ExplicitInterfaceSpecifier;
Debug.Assert(explicitInterfaceSpecifier != null);
_explicitInterfaceType.CheckAllConstraints(conversions, new SourceLocation(explicitInterfaceSpecifier.Name), diagnostics);
}
}
private SourceCustomEventAccessorSymbol CreateAccessorSymbol(AccessorDeclarationSyntax syntaxOpt,
EventSymbol explicitlyImplementedEventOpt, string aliasQualifierOpt, DiagnosticBag diagnostics)
{
if (syntaxOpt == null)
{
return null;
}
return new SourceCustomEventAccessorSymbol(this, syntaxOpt, explicitlyImplementedEventOpt, aliasQualifierOpt, diagnostics);
}
}
}