diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 5d83e488b3ab7..bd1721ce525c0 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -7,6 +7,7 @@ using System.Linq; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -388,16 +389,30 @@ private IEventReferenceExpression CreateBoundEventAccessOperation(BoundEventAcce return new LazyEventReferenceExpression(@event, instance, member, _semanticModel, syntax, type, constantValue); } - private IEventAssignmentExpression CreateBoundEventAssignmentOperatorOperation(BoundEventAssignmentOperator boundEventAssignmentOperator) + private IEventReferenceExpression CreateBoundEventAccessOperation(BoundEventAssignmentOperator boundEventAssignmentOperator) { + SyntaxNode syntax = boundEventAssignmentOperator.Syntax; + // BoundEventAssignmentOperator doesn't hold on to BoundEventAccess provided during binding. + // Based on the implementation of those two bound node types, the following data can be retrieved w/o changing BoundEventAssignmentOperator: + // 1. the type of BoundEventAccess is the type of the event symbol. + // 2. the constant value of BoundEventAccess is always null. + // 3. the syntax of the boundEventAssignmentOperator is always AssignmentExpressionSyntax, so the syntax for the event reference would be the LHS of the assignment. IEventSymbol @event = boundEventAssignmentOperator.Event; - Lazy eventInstance = new Lazy(() => Create(boundEventAssignmentOperator.Event.IsStatic ? null : boundEventAssignmentOperator.ReceiverOpt)); + Lazy instance = new Lazy(() => Create(boundEventAssignmentOperator.Event.IsStatic ? null : boundEventAssignmentOperator.ReceiverOpt)); + SyntaxNode eventAccessSyntax = ((AssignmentExpressionSyntax)syntax).Left; + + return new LazyEventReferenceExpression(@event, instance, @event, _semanticModel, eventAccessSyntax, @event.Type, ConvertToOptional(null)); + } + + private IEventAssignmentExpression CreateBoundEventAssignmentOperatorOperation(BoundEventAssignmentOperator boundEventAssignmentOperator) + { + Lazy eventReference = new Lazy(() => CreateBoundEventAccessOperation(boundEventAssignmentOperator)); Lazy handlerValue = new Lazy(() => Create(boundEventAssignmentOperator.Argument)); - bool adds = boundEventAssignmentOperator.IsAddition; SyntaxNode syntax = boundEventAssignmentOperator.Syntax; + bool adds = boundEventAssignmentOperator.IsAddition; ITypeSymbol type = boundEventAssignmentOperator.Type; Optional constantValue = ConvertToOptional(boundEventAssignmentOperator.ConstantValue); - return new LazyEventAssignmentExpression(@event, eventInstance, handlerValue, adds, _semanticModel, syntax, type, constantValue); + return new LazyEventAssignmentExpression(eventReference, handlerValue, adds, _semanticModel, syntax, type, constantValue); } private IParameterReferenceExpression CreateBoundParameterOperation(BoundParameter boundParameter) diff --git a/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs b/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs index f85b305752b4d..cedddc2203009 100644 --- a/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs @@ -1125,7 +1125,7 @@ public override void M1() ); } - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/18839")] + [Fact] public void EventAndMethodReferencesCSharp() { const string source = @" @@ -1154,21 +1154,18 @@ private void Mumbler(object sender, System.EventArgs args) CreateCompilationWithMscorlib45(source, parseOptions: TestOptions.RegularWithIOperationFeature) .VerifyDiagnostics() .VerifyAnalyzerDiagnostics(new DiagnosticAnalyzer[] { new MemberReferenceAnalyzer() }, null, null, false, + // Bug: we are missing diagnostics of "MethodBindingDescriptor" here. https://github.com/dotnet/roslyn/issues/20095 Diagnostic(MemberReferenceAnalyzer.HandlerAddedDescriptor.Id, "Mumble += new MumbleEventHandler(Mumbler)").WithLocation(10, 9), - // Bug: Missing a EventReferenceExpression here https://github.com/dotnet/roslyn/issues/8346 - Diagnostic(MemberReferenceAnalyzer.MethodBindingDescriptor.Id, "Mumbler").WithLocation(10, 42), + Diagnostic(MemberReferenceAnalyzer.EventReferenceDescriptor.Id, "Mumble").WithLocation(10, 9), Diagnostic(MemberReferenceAnalyzer.HandlerAddedDescriptor.Id, "Mumble += (s, a) => {}").WithLocation(11, 9), - // Bug: Missing a EventReferenceExpression here https://github.com/dotnet/roslyn/issues/8346 + Diagnostic(MemberReferenceAnalyzer.EventReferenceDescriptor.Id, "Mumble").WithLocation(11, 9), Diagnostic(MemberReferenceAnalyzer.HandlerAddedDescriptor.Id, "Mumble += new MumbleEventHandler((s, a) => {})").WithLocation(12, 9), - // Bug: Missing a EventReferenceExpression here https://github.com/dotnet/roslyn/issues/8346 - Diagnostic(MemberReferenceAnalyzer.MethodBindingDescriptor.Id, "(s, a) => {}").WithLocation(12, 42), // Bug: this is not a method binding https://github.com/dotnet/roslyn/issues/8347 + Diagnostic(MemberReferenceAnalyzer.EventReferenceDescriptor.Id, "Mumble").WithLocation(12, 9), Diagnostic(MemberReferenceAnalyzer.EventReferenceDescriptor.Id, "Mumble").WithLocation(13, 9), Diagnostic(MemberReferenceAnalyzer.EventReferenceDescriptor.Id, "Mumble").WithLocation(14, 20), Diagnostic(MemberReferenceAnalyzer.MethodBindingDescriptor.Id, "Mumbler").WithLocation(15, 32), Diagnostic(MemberReferenceAnalyzer.HandlerRemovedDescriptor.Id, "Mumble -= new MumbleEventHandler(Mumbler)").WithLocation(17, 9), - // Bug: Missing a EventReferenceExpression here https://github.com/dotnet/roslyn/issues/8346 - Diagnostic(MemberReferenceAnalyzer.MethodBindingDescriptor.Id, "Mumbler").WithLocation(17, 42) - ); + Diagnostic(MemberReferenceAnalyzer.EventReferenceDescriptor.Id, "Mumble").WithLocation(17, 9)); } [Fact] @@ -1484,11 +1481,11 @@ void Goo() CreateCompilationWithMscorlib45(source, parseOptions: TestOptions.RegularWithIOperationFeature) .VerifyDiagnostics(Diagnostic(ErrorCode.WRN_UnreferencedEvent, "E").WithArguments("D.E").WithLocation(6, 32)) .VerifyAnalyzerDiagnostics(new DiagnosticAnalyzer[] { new StaticMemberTestAnalyzer() }, null, null, false, - Diagnostic(StaticMemberTestAnalyzer.StaticMemberDescriptor.Id, "C.E += D.Method").WithLocation(23, 9), + Diagnostic(StaticMemberTestAnalyzer.StaticMemberDescriptor.Id, "C.E").WithLocation(23, 9), Diagnostic(StaticMemberTestAnalyzer.StaticMemberDescriptor.Id, "D.Method").WithLocation(23, 16), Diagnostic(StaticMemberTestAnalyzer.StaticMemberDescriptor.Id, "C.E").WithLocation(24, 9), Diagnostic(StaticMemberTestAnalyzer.StaticMemberDescriptor.Id, "C.Bar()").WithLocation(25, 9), - Diagnostic(StaticMemberTestAnalyzer.StaticMemberDescriptor.Id, "D.E += () => { }").WithLocation(27, 9), + Diagnostic(StaticMemberTestAnalyzer.StaticMemberDescriptor.Id, "D.E").WithLocation(27, 9), Diagnostic(StaticMemberTestAnalyzer.StaticMemberDescriptor.Id, "D.Field").WithLocation(28, 9), Diagnostic(StaticMemberTestAnalyzer.StaticMemberDescriptor.Id, "D.Property").WithLocation(29, 17), Diagnostic(StaticMemberTestAnalyzer.StaticMemberDescriptor.Id, "D.Method()").WithLocation(30, 9) diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IEventAssignmentExpression.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IEventAssignmentExpression.cs new file mode 100644 index 0000000000000..34cc6d7186d16 --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IEventAssignmentExpression.cs @@ -0,0 +1,339 @@ +// 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 Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + public partial class IOperationTests : SemanticModelTestBase + { + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void AddEventHandler() + { + string source = @" +using System; + +class Test +{ + public event EventHandler MyEvent; +} + +class C +{ + void Handler(object sender, EventArgs e) + { + } + + void M() + { + var t = new Test(); + /**/t.MyEvent += Handler/**/; + } +} +"; + string expectedOperationTree = @" +IEventAssignmentExpression (EventAdd)) (OperationKind.EventAssignmentExpression, Type: System.Void) (Syntax: 't.MyEvent += Handler') + Event Reference: IEventReferenceExpression: event System.EventHandler Test.MyEvent (OperationKind.EventReferenceExpression, Type: System.EventHandler) (Syntax: 't.MyEvent') + Instance Receiver: ILocalReferenceExpression: t (OperationKind.LocalReferenceExpression, Type: Test) (Syntax: 't') + Handler: IMethodBindingExpression: void C.Handler(System.Object sender, System.EventArgs e) (OperationKind.MethodBindingExpression, Type: System.EventHandler) (Syntax: 'Handler') + Instance Receiver: IInstanceReferenceExpression (InstanceReferenceKind.Implicit) (OperationKind.InstanceReferenceExpression, Type: C) (Syntax: 'Handler') +"; + var expectedDiagnostics = new[] { + // file.cs(6,31): warning CS0067: The event 'Test.MyEvent' is never used + // public event EventHandler MyEvent; + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "MyEvent").WithArguments("Test.MyEvent").WithLocation(6, 31) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void RemoveEventHandler() + { + string source = @" +using System; + +class Test +{ + public event EventHandler MyEvent; +} + +class C +{ + void M() + { + var t = new Test(); + /**/t.MyEvent -= null/**/; + } +} +"; + string expectedOperationTree = @" +IEventAssignmentExpression (EventRemove)) (OperationKind.EventAssignmentExpression, Type: System.Void) (Syntax: 't.MyEvent -= null') + Event Reference: IEventReferenceExpression: event System.EventHandler Test.MyEvent (OperationKind.EventReferenceExpression, Type: System.EventHandler) (Syntax: 't.MyEvent') + Instance Receiver: ILocalReferenceExpression: t (OperationKind.LocalReferenceExpression, Type: Test) (Syntax: 't') + Handler: IConversionExpression (Implicit, TryCast: False, Unchecked) (OperationKind.ConversionExpression, Type: System.EventHandler, Constant: null) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: ILiteralExpression (Text: null) (OperationKind.LiteralExpression, Type: null, Constant: null) (Syntax: 'null') +"; + var expectedDiagnostics = new[] { + // file.cs(6,31): warning CS0067: The event 'Test.MyEvent' is never used + // public event EventHandler MyEvent; + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "MyEvent").WithArguments("Test.MyEvent").WithLocation(6, 31) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void AddEventHandler_StaticEvent() + { + string source = @" +using System; + +class Test +{ + public static event EventHandler MyEvent; +} + +class C +{ + void Handler(object sender, EventArgs e) + { + } + + void M() + { + /**/Test.MyEvent += Handler/**/; + } +} +"; + string expectedOperationTree = @" +IEventAssignmentExpression (EventAdd)) (OperationKind.EventAssignmentExpression, Type: System.Void) (Syntax: 'Test.MyEvent += Handler') + Event Reference: IEventReferenceExpression: event System.EventHandler Test.MyEvent (Static) (OperationKind.EventReferenceExpression, Type: System.EventHandler) (Syntax: 'Test.MyEvent') + Instance Receiver: null + Handler: IMethodBindingExpression: void C.Handler(System.Object sender, System.EventArgs e) (OperationKind.MethodBindingExpression, Type: System.EventHandler) (Syntax: 'Handler') + Instance Receiver: IInstanceReferenceExpression (InstanceReferenceKind.Implicit) (OperationKind.InstanceReferenceExpression, Type: C) (Syntax: 'Handler') +"; + var expectedDiagnostics = new[] { + // file.cs(6,38): warning CS0067: The event 'Test.MyEvent' is never used + // public static event EventHandler MyEvent; + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "MyEvent").WithArguments("Test.MyEvent").WithLocation(6, 38) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void RemoveEventHandler_StaticEvent() + { + string source = @" +using System; + +class Test +{ + public static event EventHandler MyEvent; +} + +class C +{ + void Handler(object sender, EventArgs e) + { + } + + void M() + { + /**/Test.MyEvent -= Handler/**/; + } +} +"; + string expectedOperationTree = @" +IEventAssignmentExpression (EventRemove)) (OperationKind.EventAssignmentExpression, Type: System.Void) (Syntax: 'Test.MyEvent -= Handler') + Event Reference: IEventReferenceExpression: event System.EventHandler Test.MyEvent (Static) (OperationKind.EventReferenceExpression, Type: System.EventHandler) (Syntax: 'Test.MyEvent') + Instance Receiver: null + Handler: IMethodBindingExpression: void C.Handler(System.Object sender, System.EventArgs e) (OperationKind.MethodBindingExpression, Type: System.EventHandler) (Syntax: 'Handler') + Instance Receiver: IInstanceReferenceExpression (InstanceReferenceKind.Implicit) (OperationKind.InstanceReferenceExpression, Type: C) (Syntax: 'Handler') +"; + var expectedDiagnostics = new[] { + // file.cs(6,38): warning CS0067: The event 'Test.MyEvent' is never used + // public static event EventHandler MyEvent; + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "MyEvent").WithArguments("Test.MyEvent").WithLocation(6, 38) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void AddEventHandler_DelegateTypeMismatch() + { + string source = @" +using System; + +class Test +{ + public event EventHandler MyEvent; +} + +class C +{ + void Handler(object sender) + { + } + + void M() + { + var t = new Test(); + /**/t.MyEvent += Handler/**/; + } +} +"; + string expectedOperationTree = @" +IEventAssignmentExpression (EventAdd)) (OperationKind.EventAssignmentExpression, Type: System.Void, IsInvalid) (Syntax: 't.MyEvent += Handler') + Event Reference: IEventReferenceExpression: event System.EventHandler Test.MyEvent (OperationKind.EventReferenceExpression, Type: System.EventHandler, IsInvalid) (Syntax: 't.MyEvent') + Instance Receiver: ILocalReferenceExpression: t (OperationKind.LocalReferenceExpression, Type: Test, IsInvalid) (Syntax: 't') + Handler: IConversionExpression (Implicit, TryCast: False, Unchecked) (OperationKind.ConversionExpression, Type: System.EventHandler, IsInvalid) (Syntax: 'Handler') + Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: IOperation: (OperationKind.None, IsInvalid) (Syntax: 'Handler') +"; + var expectedDiagnostics = new[] { + // file.cs(18,19): error CS0123: No overload for 'Handler' matches delegate 'EventHandler' + // /**/t.MyEvent += Handler/**/; + Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "t.MyEvent += Handler").WithArguments("Handler", "System.EventHandler").WithLocation(18, 19), + // file.cs(6,31): warning CS0067: The event 'Test.MyEvent' is never used + // public event EventHandler MyEvent; + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "MyEvent").WithArguments("Test.MyEvent").WithLocation(6, 31) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void AddEventHandler_AssignToStaticEventOnInstance() + { + string source = @" +using System; + +class Test +{ + public static event EventHandler MyEvent; +} + +class C +{ + void Handler(object sender, EventArgs e) + { + } + + void M() + { + var t = new Test(); + /**/t.MyEvent += Handler/**/; + } +} +"; + string expectedOperationTree = @" +IEventAssignmentExpression (EventAdd)) (OperationKind.EventAssignmentExpression, Type: System.Void, IsInvalid) (Syntax: 't.MyEvent += Handler') + Event Reference: IEventReferenceExpression: event System.EventHandler Test.MyEvent (Static) (OperationKind.EventReferenceExpression, Type: System.EventHandler, IsInvalid) (Syntax: 't.MyEvent') + Instance Receiver: null + Handler: IMethodBindingExpression: void C.Handler(System.Object sender, System.EventArgs e) (OperationKind.MethodBindingExpression, Type: System.EventHandler) (Syntax: 'Handler') + Instance Receiver: IInstanceReferenceExpression (InstanceReferenceKind.Implicit) (OperationKind.InstanceReferenceExpression, Type: C) (Syntax: 'Handler') +"; + var expectedDiagnostics = new[] { + // file.cs(18,19): error CS0176: Member 'Test.MyEvent' cannot be accessed with an instance reference; qualify it with a type name instead + // /**/t.MyEvent += Handler/**/; + Diagnostic(ErrorCode.ERR_ObjectProhibited, "t.MyEvent").WithArguments("Test.MyEvent").WithLocation(18, 19), + // file.cs(6,38): warning CS0067: The event 'Test.MyEvent' is never used + // public static event EventHandler MyEvent; + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "MyEvent").WithArguments("Test.MyEvent").WithLocation(6, 38) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + [WorkItem(8909, "https://github.com/dotnet/roslyn/issues/8909")] + public void AddEventHandler_AssignToNonStaticEventOnType() + { + string source = @" +using System; + +class Test +{ + public event EventHandler MyEvent; +} + +class C +{ + void Handler(object sender, EventArgs e) + { + } + + void M() + { + /**/Test.MyEvent += Handler/**/; + } +} +"; + string expectedOperationTree = @" +IEventAssignmentExpression (EventAdd)) (OperationKind.EventAssignmentExpression, Type: System.Void, IsInvalid) (Syntax: 'Test.MyEvent += Handler') + Event Reference: IEventReferenceExpression: event System.EventHandler Test.MyEvent (OperationKind.EventReferenceExpression, Type: System.EventHandler, IsInvalid) (Syntax: 'Test.MyEvent') + Instance Receiver: IOperation: (OperationKind.None, IsInvalid) (Syntax: 'Test') + Handler: IMethodBindingExpression: void C.Handler(System.Object sender, System.EventArgs e) (OperationKind.MethodBindingExpression, Type: System.EventHandler) (Syntax: 'Handler') + Instance Receiver: IInstanceReferenceExpression (InstanceReferenceKind.Implicit) (OperationKind.InstanceReferenceExpression, Type: C) (Syntax: 'Handler')"; + var expectedDiagnostics = new[] { + // file.cs(17,19): error CS0120: An object reference is required for the non-static field, method, or property 'Test.MyEvent' + // /**/Test.MyEvent += Handler/**/; + Diagnostic(ErrorCode.ERR_ObjectRequired, "Test.MyEvent").WithArguments("Test.MyEvent").WithLocation(17, 19), + // file.cs(6,31): warning CS0067: The event 'Test.MyEvent' is never used + // public event EventHandler MyEvent; + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "MyEvent").WithArguments("Test.MyEvent").WithLocation(6, 31) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void AddEventHandler_AssignToEventWithoutExplicitReceiver() + { + string source = @" +using System; + +class Test +{ + public event EventHandler MyEvent; + + void Handler(object sender, EventArgs e) + { + } + + void M() + { + /**/MyEvent += Handler/**/; + } +} +"; + string expectedOperationTree = @" +IEventAssignmentExpression (EventAdd)) (OperationKind.EventAssignmentExpression, Type: System.Void) (Syntax: 'MyEvent += Handler') + Event Reference: IEventReferenceExpression: event System.EventHandler Test.MyEvent (OperationKind.EventReferenceExpression, Type: System.EventHandler) (Syntax: 'MyEvent') + Instance Receiver: IInstanceReferenceExpression (InstanceReferenceKind.Implicit) (OperationKind.InstanceReferenceExpression, Type: Test) (Syntax: 'MyEvent') + Handler: IMethodBindingExpression: void Test.Handler(System.Object sender, System.EventArgs e) (OperationKind.MethodBindingExpression, Type: System.EventHandler) (Syntax: 'Handler') + Instance Receiver: IInstanceReferenceExpression (InstanceReferenceKind.Implicit) (OperationKind.InstanceReferenceExpression, Type: Test) (Syntax: 'Handler')"; + var expectedDiagnostics = new[] { + // file.cs(6,31): warning CS0067: The event 'Test.MyEvent' is never used + // public event EventHandler MyEvent; + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "MyEvent").WithArguments("Test.MyEvent").WithLocation(6, 31) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + } +} diff --git a/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs index 86a96d452548f..2db8a93319f5b 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.xml.Generated.cs @@ -1328,17 +1328,20 @@ public override TResult Accept(OperationVisitor internal abstract partial class BaseEventAssignmentExpression : Operation, IEventAssignmentExpression { - protected BaseEventAssignmentExpression(IEventSymbol @event, bool adds, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional constantValue) : + protected BaseEventAssignmentExpression(bool adds, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional constantValue) : base(OperationKind.EventAssignmentExpression, semanticModel, syntax, type, constantValue) { - Event = @event; Adds = adds; } + /// - /// Event being bound. + /// Reference to the event being bound. + /// + protected abstract IEventReferenceExpression EventReferenceImpl { get; } + + /// + /// Handler supplied for the event. /// - public IEventSymbol Event { get; } - protected abstract IOperation EventInstanceImpl { get; } protected abstract IOperation HandlerValueImpl { get; } /// @@ -1349,7 +1352,7 @@ public override IEnumerable Children { get { - yield return EventInstance; + yield return EventReference; yield return HandlerValue; } } @@ -1357,7 +1360,7 @@ public override IEnumerable Children /// /// Instance used to refer to the event being bound. /// - public IOperation EventInstance => Operation.SetParentOperation(EventInstanceImpl, this); + public IEventReferenceExpression EventReference => Operation.SetParentOperation(EventReferenceImpl, this); /// /// Handler supplied for the event. @@ -1378,14 +1381,14 @@ public override TResult Accept(OperationVisitor internal sealed partial class EventAssignmentExpression : BaseEventAssignmentExpression, IEventAssignmentExpression { - public EventAssignmentExpression(IEventSymbol @event, IOperation eventInstance, IOperation handlerValue, bool adds, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional constantValue) : - base(@event, adds, semanticModel, syntax, type, constantValue) + public EventAssignmentExpression(IEventReferenceExpression eventReference, IOperation handlerValue, bool adds, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional constantValue) : + base(adds, semanticModel, syntax, type, constantValue) { - EventInstanceImpl = eventInstance; + EventReferenceImpl = eventReference; HandlerValueImpl = handlerValue; } - protected override IOperation EventInstanceImpl { get; } + protected override IEventReferenceExpression EventReferenceImpl { get; } protected override IOperation HandlerValueImpl { get; } } @@ -1394,16 +1397,17 @@ public EventAssignmentExpression(IEventSymbol @event, IOperation eventInstance, /// internal sealed partial class LazyEventAssignmentExpression : BaseEventAssignmentExpression, IEventAssignmentExpression { - private readonly Lazy _lazyEventInstance; + private readonly Lazy _lazyEventReference; private readonly Lazy _lazyHandlerValue; + + public LazyEventAssignmentExpression(Lazy eventReference, Lazy handlerValue, bool adds, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional constantValue) : base(adds, semanticModel, syntax, type, constantValue) - public LazyEventAssignmentExpression(IEventSymbol @event, Lazy eventInstance, Lazy handlerValue, bool adds, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional constantValue) : base(@event, adds, semanticModel, syntax, type, constantValue) { - _lazyEventInstance = eventInstance ?? throw new System.ArgumentNullException(nameof(eventInstance)); + _lazyEventReference = eventReference ?? throw new System.ArgumentNullException(nameof(eventReference)); _lazyHandlerValue = handlerValue ?? throw new System.ArgumentNullException(nameof(handlerValue)); } - - protected override IOperation EventInstanceImpl => _lazyEventInstance.Value; + + protected override IEventReferenceExpression EventReferenceImpl => _lazyEventReference.Value; protected override IOperation HandlerValueImpl => _lazyHandlerValue.Value; } diff --git a/src/Compilers/Core/Portable/Operations/IEventAssignmentExpression.cs b/src/Compilers/Core/Portable/Operations/IEventAssignmentExpression.cs index 6845383637c4b..577a20b253479 100644 --- a/src/Compilers/Core/Portable/Operations/IEventAssignmentExpression.cs +++ b/src/Compilers/Core/Portable/Operations/IEventAssignmentExpression.cs @@ -1,7 +1,5 @@ // 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; - namespace Microsoft.CodeAnalysis.Semantics { /// @@ -14,15 +12,10 @@ namespace Microsoft.CodeAnalysis.Semantics public interface IEventAssignmentExpression : IOperation { /// - /// Event being bound. + /// Reference to the event being bound. /// - IEventSymbol Event { get; } - - /// - /// Instance used to refer to the event being bound. - /// - IOperation EventInstance { get; } - + IEventReferenceExpression EventReference { get; } + /// /// Handler supplied for the event. /// diff --git a/src/Compilers/Core/Portable/Operations/IEventReferenceExpression.cs b/src/Compilers/Core/Portable/Operations/IEventReferenceExpression.cs index ac77aa6aefa4f..0a7e08393aeb6 100644 --- a/src/Compilers/Core/Portable/Operations/IEventReferenceExpression.cs +++ b/src/Compilers/Core/Portable/Operations/IEventReferenceExpression.cs @@ -1,7 +1,5 @@ // 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; - namespace Microsoft.CodeAnalysis.Semantics { /// diff --git a/src/Compilers/Core/Portable/Operations/OperationCloner.cs b/src/Compilers/Core/Portable/Operations/OperationCloner.cs index ac29e87e10273..038de6c65fd1c 100644 --- a/src/Compilers/Core/Portable/Operations/OperationCloner.cs +++ b/src/Compilers/Core/Portable/Operations/OperationCloner.cs @@ -241,7 +241,7 @@ public override IOperation VisitEventReferenceExpression(IEventReferenceExpressi public override IOperation VisitEventAssignmentExpression(IEventAssignmentExpression operation, object argument) { - return new EventAssignmentExpression(operation.Event, Visit(operation.EventInstance), Visit(operation.HandlerValue), operation.Adds, ((Operation)operation).SemanticModel, operation.Syntax, operation.Type, operation.ConstantValue); + return new EventAssignmentExpression(Visit(operation.EventReference), Visit(operation.HandlerValue), operation.Adds, ((Operation)operation).SemanticModel, operation.Syntax, operation.Type, operation.ConstantValue); } public override IOperation VisitConditionalAccessExpression(IConditionalAccessExpression operation, object argument) diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 20c3b5d1c2dee..a9d2055d6d0f1 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -419,8 +419,7 @@ Microsoft.CodeAnalysis.Semantics.IEmptyStatement Microsoft.CodeAnalysis.Semantics.IEndStatement Microsoft.CodeAnalysis.Semantics.IEventAssignmentExpression Microsoft.CodeAnalysis.Semantics.IEventAssignmentExpression.Adds.get -> bool -Microsoft.CodeAnalysis.Semantics.IEventAssignmentExpression.Event.get -> Microsoft.CodeAnalysis.IEventSymbol -Microsoft.CodeAnalysis.Semantics.IEventAssignmentExpression.EventInstance.get -> Microsoft.CodeAnalysis.IOperation +Microsoft.CodeAnalysis.Semantics.IEventAssignmentExpression.EventReference.get -> Microsoft.CodeAnalysis.Semantics.IEventReferenceExpression Microsoft.CodeAnalysis.Semantics.IEventAssignmentExpression.HandlerValue.get -> Microsoft.CodeAnalysis.IOperation Microsoft.CodeAnalysis.Semantics.IEventReferenceExpression Microsoft.CodeAnalysis.Semantics.IEventReferenceExpression.Event.get -> Microsoft.CodeAnalysis.IEventSymbol diff --git a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb index 3f61db0e1f95d..8eed84d14b050 100644 --- a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb +++ b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory.vb @@ -1104,7 +1104,7 @@ Namespace Microsoft.CodeAnalysis.Semantics End Function Private Function CreateBoundAddHandlerStatementOperation(boundAddHandlerStatement As BoundAddHandlerStatement) As IExpressionStatement - Dim expression As Lazy(Of IOperation) = New Lazy(Of IOperation)(Function() GetAddHandlerStatementExpression(boundAddHandlerStatement)) + Dim expression As Lazy(Of IOperation) = New Lazy(Of IOperation)(Function() GetAddRemoveHandlerStatementExpression(boundAddHandlerStatement)) Dim syntax As SyntaxNode = boundAddHandlerStatement.Syntax Dim type As ITypeSymbol = Nothing Dim constantValue As [Optional](Of Object) = New [Optional](Of Object)() @@ -1112,7 +1112,7 @@ Namespace Microsoft.CodeAnalysis.Semantics End Function Private Function CreateBoundRemoveHandlerStatementOperation(boundRemoveHandlerStatement As BoundRemoveHandlerStatement) As IExpressionStatement - Dim expression As Lazy(Of IOperation) = New Lazy(Of IOperation)(Function() GetRemoveStatementExpression(boundRemoveHandlerStatement)) + Dim expression As Lazy(Of IOperation) = New Lazy(Of IOperation)(Function() GetAddRemoveHandlerStatementExpression(boundRemoveHandlerStatement)) Dim syntax As SyntaxNode = boundRemoveHandlerStatement.Syntax Dim type As ITypeSymbol = Nothing Dim constantValue As [Optional](Of Object) = New [Optional](Of Object)() diff --git a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory_Methods.vb b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory_Methods.vb index 7c53869c2b6f7..9bcea721014ad 100644 --- a/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory_Methods.vb +++ b/src/Compilers/VisualBasic/Portable/Operations/VisualBasicOperationFactory_Methods.vb @@ -425,22 +425,12 @@ Namespace Microsoft.CodeAnalysis.Semantics constantValue:=Nothing) End Function - Private Function GetAddHandlerStatementExpression(statement As BoundAddHandlerStatement) As IOperation + Private Function GetAddRemoveHandlerStatementExpression(statement As BoundAddRemoveHandlerStatement) As IOperation Dim eventAccess As BoundEventAccess = TryCast(statement.EventAccess, BoundEventAccess) - Dim [event] As IEventSymbol = eventAccess?.EventSymbol - Dim instance = If([event] Is Nothing OrElse [event].IsStatic, Nothing, If(eventAccess IsNot Nothing, Create(eventAccess.ReceiverOpt), Nothing)) - - Return New EventAssignmentExpression( - [event], instance, Create(statement.Handler), adds:=True, semanticModel:=_semanticModel, syntax:=statement.Syntax, type:=Nothing, constantValue:=Nothing) - End Function - - Private Function GetRemoveStatementExpression(statement As BoundRemoveHandlerStatement) As IOperation - Dim eventAccess As BoundEventAccess = TryCast(statement.EventAccess, BoundEventAccess) - Dim [event] As IEventSymbol = eventAccess?.EventSymbol - Dim instance = If([event] Is Nothing OrElse [event].IsStatic, Nothing, If(eventAccess IsNot Nothing, Create(eventAccess.ReceiverOpt), Nothing)) - + Dim eventReference = If(eventAccess Is Nothing, Nothing, CreateBoundEventAccessOperation(eventAccess)) + Dim adds = statement.Kind = BoundKind.AddHandlerStatement Return New EventAssignmentExpression( - [event], instance, Create(statement.Handler), adds:=False, semanticModel:=_semanticModel, syntax:=statement.Syntax, type:=Nothing, constantValue:=Nothing) + eventReference, Create(statement.Handler), adds:=adds, semanticModel:=_semanticModel, syntax:=statement.Syntax, type:=Nothing, constantValue:=Nothing) End Function Private Shared Function GetConversionKind(kind As VisualBasic.ConversionKind) As Semantics.ConversionKind diff --git a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/OperationAnalyzerTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/OperationAnalyzerTests.vb index 43ddd40408d79..de9ddfaa90f6b 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/OperationAnalyzerTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/OperationAnalyzerTests.vb @@ -1146,7 +1146,7 @@ End Class Diagnostic(ExplicitVsImplicitInstanceAnalyzer.ImplicitInstanceDescriptor.Id, "M2").WithLocation(15, 9)) End Sub - + Public Sub EventAndMethodReferencesVisualBasic() Dim source = @@ -1178,17 +1178,19 @@ End Class Dim comp = CompilationUtils.CreateCompilationWithMscorlibAndVBRuntime(source, parseOptions:=TestOptions.RegularWithIOperationFeature) comp.VerifyDiagnostics() - comp.VerifyAnalyzerDiagnostics({New MemberReferenceAnalyzer}, Nothing, Nothing, False, - Diagnostic(MemberReferenceAnalyzer.HandlerAddedDescriptor.Id, "AddHandler Mumble, New MumbleEventHandler(AddressOf Mumbler)").WithLocation(7, 9), ' Bug: Missing a EventReferenceExpression here https://github.com/dotnet/roslyn/issues/8346 - Diagnostic(MemberReferenceAnalyzer.MethodBindingDescriptor.Id, "AddressOf Mumbler").WithLocation(7, 51), + comp.VerifyAnalyzerDiagnostics({New MemberReferenceAnalyzer}, Nothing, Nothing, False, ' Bug: we are missing diagnostics of "MethodBindingDescriptor" here. https://github.com/dotnet/roslyn/issues/20095 + Diagnostic(MemberReferenceAnalyzer.HandlerAddedDescriptor.Id, "AddHandler Mumble, New MumbleEventHandler(AddressOf Mumbler)").WithLocation(7, 9), + Diagnostic(MemberReferenceAnalyzer.EventReferenceDescriptor.Id, "Mumble").WithLocation(7, 20), Diagnostic(MemberReferenceAnalyzer.HandlerAddedDescriptor.Id, "AddHandler Mumble, New MumbleEventHandler(Sub(s As Object, a As System.EventArgs) - End Sub)").WithLocation(8, 9), ' Bug: Missing a EventReferenceExpression here https://github.com/dotnet/roslyn/issues/8346 + End Sub)").WithLocation(8, 9), + Diagnostic(MemberReferenceAnalyzer.EventReferenceDescriptor.Id, "Mumble").WithLocation(8, 20), Diagnostic(MemberReferenceAnalyzer.HandlerAddedDescriptor.Id, "AddHandler Mumble, Sub(s As Object, a As System.EventArgs) - End Sub").WithLocation(10, 9), ' Bug: Missing a EventReferenceExpression here https://github.com/dotnet/roslyn/issues/8346 + End Sub").WithLocation(10, 9), + Diagnostic(MemberReferenceAnalyzer.EventReferenceDescriptor.Id, "Mumble").WithLocation(10, 20), Diagnostic(MemberReferenceAnalyzer.FieldReferenceDescriptor.Id, "Mumble").WithLocation(12, 20), ' Bug: This should be an event reference. https://github.com/dotnet/roslyn/issues/8345 - Diagnostic(MemberReferenceAnalyzer.MethodBindingDescriptor.Id, "AddressOf Mumbler").WithLocation(14, 39), - Diagnostic(MemberReferenceAnalyzer.HandlerRemovedDescriptor.Id, "RemoveHandler Mumble, AddressOf Mumbler").WithLocation(16, 9), ' Bug: Missing a EventReferenceExpression here https://github.com/dotnet/roslyn/issues/8346 - Diagnostic(MemberReferenceAnalyzer.MethodBindingDescriptor.Id, "AddressOf Mumbler").WithLocation(16, 31)) + Diagnostic(MemberReferenceAnalyzer.HandlerRemovedDescriptor.Id, "RemoveHandler Mumble, AddressOf Mumbler").WithLocation(16, 9), + Diagnostic(MemberReferenceAnalyzer.EventReferenceDescriptor.Id, "Mumble").WithLocation(16, 23) + ) End Sub diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IEventAssignmentExpression.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IEventAssignmentExpression.vb new file mode 100644 index 0000000000000..2e30be4d75413 --- /dev/null +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IEventAssignmentExpression.vb @@ -0,0 +1,251 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports Microsoft.CodeAnalysis.Test.Utilities +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Roslyn.Test.Utilities + +Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Semantics + + Partial Public Class IOperationTests + Inherits SemanticModelTestBase + + + + Public Sub AddEventHandler() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AddRemoveHandlerStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub RemoveEventHandler() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AddRemoveHandlerStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub AddEventHandler_StaticEvent() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AddRemoveHandlerStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub RemoveEventHandler_StaticEvent() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AddRemoveHandlerStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub RemoveEventHandler_DelegateTypeMismatch() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + VerifyOperationTreeAndDiagnosticsForTest(Of AddRemoveHandlerStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + Public Sub AddEventHandler_AssignToSharedEventOnInstance() + Dim source = .Value + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + VerifyOperationTreeAndDiagnosticsForTest(Of AddRemoveHandlerStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + + + Public Sub AddEventHandler_AssignToNonSharedEventOnType() + Dim source = .Value + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + VerifyOperationTreeAndDiagnosticsForTest(Of AddRemoveHandlerStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + End Class +End Namespace diff --git a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs index d53058d67608f..6b7250fa0b2d6 100644 --- a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs +++ b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs @@ -740,11 +740,10 @@ public override void VisitEventAssignmentExpression(IEventAssignmentExpression o { var kindStr = operation.Adds ? "EventAdd" : "EventRemove"; LogString($"{nameof(IEventAssignmentExpression)} ({kindStr})"); - LogSymbol(operation.Event, header: " (Event: "); LogString(")"); LogCommonPropertiesAndNewLine(operation); - Visit(operation.EventInstance, header: "Event Instance"); + Visit(operation.EventReference, header: "Event Reference"); Visit(operation.HandlerValue, header: "Handler"); } diff --git a/src/Test/Utilities/Portable/Compilation/TestOperationWalker.cs b/src/Test/Utilities/Portable/Compilation/TestOperationWalker.cs index 49cc0ccb5d256..ae9cdd4978e9c 100644 --- a/src/Test/Utilities/Portable/Compilation/TestOperationWalker.cs +++ b/src/Test/Utilities/Portable/Compilation/TestOperationWalker.cs @@ -305,7 +305,6 @@ public override void VisitEventReferenceExpression(IEventReferenceExpression ope public override void VisitEventAssignmentExpression(IEventAssignmentExpression operation) { - var eventSymbol = operation.Event; var adds = operation.Adds; base.VisitEventAssignmentExpression(operation); diff --git a/src/Test/Utilities/Portable/Diagnostics/OperationTestAnalyzer.cs b/src/Test/Utilities/Portable/Diagnostics/OperationTestAnalyzer.cs index d2e4a753e6c47..dc3600b9ad6f3 100644 --- a/src/Test/Utilities/Portable/Diagnostics/OperationTestAnalyzer.cs +++ b/src/Test/Utilities/Portable/Diagnostics/OperationTestAnalyzer.cs @@ -1061,13 +1061,9 @@ public sealed override void Initialize(AnalysisContext context) IEventAssignmentExpression eventAssignment = (IEventAssignmentExpression)operationContext.Operation; operationContext.ReportDiagnostic(Diagnostic.Create(eventAssignment.Adds ? HandlerAddedDescriptor : HandlerRemovedDescriptor, operationContext.Operation.Syntax.GetLocation())); - if (eventAssignment.Event == null) + if (eventAssignment.EventReference?.Event == null && eventAssignment.HasErrors(operationContext.Compilation, operationContext.CancellationToken)) { - if (eventAssignment.EventInstance == null && eventAssignment.HasErrors(operationContext.Compilation, operationContext.CancellationToken)) - { - // report inside after checking for null to make sure it does't crash. - operationContext.ReportDiagnostic(Diagnostic.Create(InvalidEventDescriptor, eventAssignment.Syntax.GetLocation())); - } + operationContext.ReportDiagnostic(Diagnostic.Create(InvalidEventDescriptor, eventAssignment.Syntax.GetLocation())); } }, OperationKind.EventAssignmentExpression); @@ -1426,10 +1422,6 @@ public sealed override void Initialize(AnalysisContext context) memberSymbol = ((IInvocationExpression)operation).TargetMethod; receiver = ((IInvocationExpression)operation).Instance; break; - case OperationKind.EventAssignmentExpression: - memberSymbol = ((IEventAssignmentExpression)operation).Event; - receiver = ((IEventAssignmentExpression)operation).EventInstance; - break; default: throw new ArgumentException(); } @@ -1447,8 +1439,7 @@ public sealed override void Initialize(AnalysisContext context) OperationKind.PropertyReferenceExpression, OperationKind.EventReferenceExpression, OperationKind.MethodBindingExpression, - OperationKind.InvocationExpression, - OperationKind.EventAssignmentExpression); + OperationKind.InvocationExpression); } }