diff --git a/src/NSubstitute.Analyzers.Shared/Extensions/IOperationExtensions.cs b/src/NSubstitute.Analyzers.Shared/Extensions/IOperationExtensions.cs index 0f52ab22..696f5c4b 100644 --- a/src/NSubstitute.Analyzers.Shared/Extensions/IOperationExtensions.cs +++ b/src/NSubstitute.Analyzers.Shared/Extensions/IOperationExtensions.cs @@ -114,12 +114,11 @@ public static IEnumerable Ancestors(this IOperation operation) { var symbol = operation switch { + IMemberReferenceOperation memberReferenceOperation => memberReferenceOperation.Member, IInvocationOperation invocationOperation => invocationOperation.TargetMethod, - IPropertyReferenceOperation propertyReferenceOperation => propertyReferenceOperation.Property, IConversionOperation conversionOperation => ExtractSymbol(conversionOperation.Operand), IAwaitOperation awaitOperation => ExtractSymbol(awaitOperation.Operation), ILocalReferenceOperation localReferenceOperation => localReferenceOperation.Local, - IFieldReferenceOperation fieldReferenceOperation => fieldReferenceOperation.Field, _ => null }; diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberReceivedAnalyzerTests/NonSubstitutableMemberReceivedDiagnosticVerifier.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberReceivedAnalyzerTests/NonSubstitutableMemberReceivedDiagnosticVerifier.cs index 66fa9d8f..6f518593 100644 --- a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberReceivedAnalyzerTests/NonSubstitutableMemberReceivedDiagnosticVerifier.cs +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberReceivedAnalyzerTests/NonSubstitutableMemberReceivedDiagnosticVerifier.cs @@ -78,6 +78,22 @@ public abstract class NonSubstitutableMemberReceivedDiagnosticVerifier : CSharpD [InlineData] public abstract Task ReportsDiagnostics_WhenCheckingReceivedCallsForNonVirtualIndexer(string method); + [CombinatoryTheory] + [InlineData] + public abstract Task ReportsDiagnostics_WhenCheckingReceivedCallsForNonVirtualEvent(string method); + + [CombinatoryTheory] + [InlineData] + public abstract Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForAbstractEvent(string method); + + [CombinatoryTheory] + [InlineData] + public abstract Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForVirtualEvent(string method); + + [CombinatoryTheory] + [InlineData] + public abstract Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForInterfaceEvent(string method); + [CombinatoryTheory] [InlineData(".Bar", "Internal member Bar can not be intercepted without InternalsVisibleToAttribute.")] [InlineData(".FooBar()", "Internal member FooBar can not be intercepted without InternalsVisibleToAttribute.")] diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberReceivedAnalyzerTests/ReceivedAsExtensionMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberReceivedAnalyzerTests/ReceivedAsExtensionMethodTests.cs index c8916136..7826d6cd 100644 --- a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberReceivedAnalyzerTests/ReceivedAsExtensionMethodTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberReceivedAnalyzerTests/ReceivedAsExtensionMethodTests.cs @@ -433,6 +433,110 @@ public void Test() await VerifyDiagnostic(source, NonVirtualReceivedSetupSpecificationDescriptor, "Member this[] can not be intercepted. Only interface members and virtual, overriding, and abstract members can be intercepted."); } + public override async Task ReportsDiagnostics_WhenCheckingReceivedCallsForNonVirtualEvent(string method) + { + var source = $@"using NSubstitute; +using NSubstitute.ReceivedExtensions; +using System; + +namespace MyNamespace +{{ + public class Foo + {{ + public event Action Event; + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + [|substitute.{method}.Event|] += () => {{ }}; + }} + }} +}}"; + + await VerifyDiagnostic(source, NonVirtualReceivedSetupSpecificationDescriptor, "Member Event can not be intercepted. Only interface members and virtual, overriding, and abstract members can be intercepted."); + } + + public override async Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForAbstractEvent(string method) + { + var source = $@"using NSubstitute; +using NSubstitute.ReceivedExtensions; +using System; + +namespace MyNamespace +{{ + public abstract class Foo + {{ + public abstract event Action Event; + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + substitute.{method}.Event += () => {{ }}; + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + public override async Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForVirtualEvent(string method) + { + var source = $@"using NSubstitute; +using NSubstitute.ReceivedExtensions; +using System; + +namespace MyNamespace +{{ + public class Foo + {{ + public virtual event Action Event; + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + substitute.{method}.Event += () => {{ }}; + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + public override async Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForInterfaceEvent(string method) + { + var source = $@"using NSubstitute; +using NSubstitute.ReceivedExtensions; +using System; + +namespace MyNamespace +{{ + public interface Foo + {{ + event Action Event; + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + substitute.{method}.Event += () => {{ }}; + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + [CombinatoryData( "Received(Quantity.None())", "Received(Quantity.None())", diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberReceivedAnalyzerTests/ReceivedAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberReceivedAnalyzerTests/ReceivedAsOrdinaryMethodTests.cs index cc832228..31930369 100644 --- a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberReceivedAnalyzerTests/ReceivedAsOrdinaryMethodTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberReceivedAnalyzerTests/ReceivedAsOrdinaryMethodTests.cs @@ -478,6 +478,110 @@ public void Test() await VerifyDiagnostic(source, NonVirtualReceivedSetupSpecificationDescriptor, "Member this[] can not be intercepted. Only interface members and virtual, overriding, and abstract members can be intercepted."); } + public override async Task ReportsDiagnostics_WhenCheckingReceivedCallsForNonVirtualEvent(string method) + { + var source = $@"using NSubstitute; +using NSubstitute.ReceivedExtensions; +using System; + +namespace MyNamespace +{{ + public class Foo + {{ + public event Action Event; + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + [|{method}.Event|] += () => {{ }}; + }} + }} +}}"; + + await VerifyDiagnostic(source, NonVirtualReceivedSetupSpecificationDescriptor, "Member Event can not be intercepted. Only interface members and virtual, overriding, and abstract members can be intercepted."); + } + + public override async Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForAbstractEvent(string method) + { + var source = $@"using NSubstitute; +using NSubstitute.ReceivedExtensions; +using System; + +namespace MyNamespace +{{ + public abstract class Foo + {{ + public abstract event Action Event; + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {method}.Event += () => {{ }}; + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + public override async Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForVirtualEvent(string method) + { + var source = $@"using NSubstitute; +using NSubstitute.ReceivedExtensions; +using System; + +namespace MyNamespace +{{ + public class Foo + {{ + public virtual event Action Event; + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {method}.Event += () => {{ }}; + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + public override async Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForInterfaceEvent(string method) + { + var source = $@"using NSubstitute; +using NSubstitute.ReceivedExtensions; +using System; + +namespace MyNamespace +{{ + public interface Foo + {{ + event Action Event; + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {method}.Event += () => {{ }}; + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + [CombinatoryData( "ReceivedExtensions.Received(substitute, Quantity.None())", "ReceivedExtensions.Received(substitute: substitute, x: Quantity.None())", diff --git a/tests/NSubstitute.Analyzers.Tests.Shared/DiagnosticAnalyzers/INonSubstitutableMemberReceivedDiagnosticVerifier.cs b/tests/NSubstitute.Analyzers.Tests.Shared/DiagnosticAnalyzers/INonSubstitutableMemberReceivedDiagnosticVerifier.cs index a77d2d10..dd895f47 100644 --- a/tests/NSubstitute.Analyzers.Tests.Shared/DiagnosticAnalyzers/INonSubstitutableMemberReceivedDiagnosticVerifier.cs +++ b/tests/NSubstitute.Analyzers.Tests.Shared/DiagnosticAnalyzers/INonSubstitutableMemberReceivedDiagnosticVerifier.cs @@ -34,6 +34,14 @@ public interface INonSubstitutableMemberReceivedDiagnosticVerifier Task ReportsDiagnostics_WhenCheckingReceivedCallsForNonVirtualIndexer(string method); + Task ReportsDiagnostics_WhenCheckingReceivedCallsForNonVirtualEvent(string method); + + Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForAbstractEvent(string method); + + Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForVirtualEvent(string method); + + Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForInterfaceEvent(string method); + Task ReportsDiagnostics_WhenSettingValueForInternalVirtualMember_AndInternalsVisibleToNotApplied(string method, string call, string message); Task ReportsNoDiagnostics_WhenSettingValueForInternalVirtualMember_AndInternalsVisibleToApplied(string method, string call); diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberReceivedAnalyzerTests/NonSubstitutableMemberReceivedDiagnosticVerifier.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberReceivedAnalyzerTests/NonSubstitutableMemberReceivedDiagnosticVerifier.cs index edd9d456..7fff9110 100644 --- a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberReceivedAnalyzerTests/NonSubstitutableMemberReceivedDiagnosticVerifier.cs +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberReceivedAnalyzerTests/NonSubstitutableMemberReceivedDiagnosticVerifier.cs @@ -78,6 +78,22 @@ public abstract class NonSubstitutableMemberReceivedDiagnosticVerifier : VisualB [InlineData] public abstract Task ReportsDiagnostics_WhenCheckingReceivedCallsForNonVirtualIndexer(string method); + [CombinatoryTheory] + [InlineData] + public abstract Task ReportsDiagnostics_WhenCheckingReceivedCallsForNonVirtualEvent(string method); + + [CombinatoryTheory(Skip = "VisualBasic does not allow to mark events as abstract (MustInherit)")] + [InlineData] + public abstract Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForAbstractEvent(string method); + + [CombinatoryTheory(Skip = "VisualBasic does not allow to mark events as virtual (Overridable)")] + [InlineData] + public abstract Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForVirtualEvent(string method); + + [CombinatoryTheory] + [InlineData] + public abstract Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForInterfaceEvent(string method); + [CombinatoryTheory] [InlineData(".Bar", "Friend member Bar can not be intercepted without InternalsVisibleToAttribute.")] [InlineData(".FooBar()", "Friend member FooBar can not be intercepted without InternalsVisibleToAttribute.")] diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberReceivedAnalyzerTests/ReceivedAsExtensionMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberReceivedAnalyzerTests/ReceivedAsExtensionMethodTests.cs index 9b38202e..0bf61a41 100644 --- a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberReceivedAnalyzerTests/ReceivedAsExtensionMethodTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberReceivedAnalyzerTests/ReceivedAsExtensionMethodTests.cs @@ -412,6 +412,66 @@ End Class await VerifyDiagnostic(source, NonVirtualReceivedSetupSpecificationDescriptor, "Member Item can not be intercepted. Only interface members and overrideable, overriding, and must override members can be intercepted."); } + public override async Task ReportsDiagnostics_WhenCheckingReceivedCallsForNonVirtualEvent(string method) + { + var source = $@"Imports NSubstitute +Imports NSubstitute.ReceivedExtensions +Imports System + +Namespace MyNamespace + Public Class Foo + Public Event SomeEvent As Action + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + AddHandler [|substitute.{method}.SomeEvent|], Sub() + End Sub + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source, NonVirtualReceivedSetupSpecificationDescriptor, "Member SomeEvent can not be intercepted. Only interface members and overrideable, overriding, and must override members can be intercepted."); + } + + public override Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForAbstractEvent(string method) + { + // VisualBasic does not allow to mark events as abstract (MustInherit) + return Task.CompletedTask; + } + + public override Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForVirtualEvent(string method) + { + // VisualBasic does not allow to mark events as virtual (Overridable) + return Task.CompletedTask; + } + + public override async Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForInterfaceEvent(string method) + { + var source = $@"Imports NSubstitute +Imports NSubstitute.ReceivedExtensions +Imports System + +Namespace MyNamespace + Public Interface Foo + Event SomeEvent As Action + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + AddHandler substitute.{method}.SomeEvent, Sub() + End Sub + End Sub + End Class +End Namespace +"; + + await VerifyNoDiagnostic(source); + } + [CombinatoryData( "Received(Quantity.None())", "Received(1, 1)", diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberReceivedAnalyzerTests/ReceivedAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberReceivedAnalyzerTests/ReceivedAsOrdinaryMethodTests.cs index ecfbc5e6..22be3179 100644 --- a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberReceivedAnalyzerTests/ReceivedAsOrdinaryMethodTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberReceivedAnalyzerTests/ReceivedAsOrdinaryMethodTests.cs @@ -491,6 +491,66 @@ End Class await VerifyDiagnostic(source, NonVirtualReceivedSetupSpecificationDescriptor, "Member Item can not be intercepted. Only interface members and overrideable, overriding, and must override members can be intercepted."); } + public override async Task ReportsDiagnostics_WhenCheckingReceivedCallsForNonVirtualEvent(string method) + { + var source = $@"Imports NSubstitute +Imports NSubstitute.ReceivedExtensions +Imports System + +Namespace MyNamespace + Public Class Foo + Public Event SomeEvent As Action + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + AddHandler [|{method}.SomeEvent|], Sub() + End Sub + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source, NonVirtualReceivedSetupSpecificationDescriptor, "Member SomeEvent can not be intercepted. Only interface members and overrideable, overriding, and must override members can be intercepted."); + } + + public override Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForAbstractEvent(string method) + { + // VisualBasic does not allow to mark events as abstract (MustInherit) + return Task.CompletedTask; + } + + public override Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForVirtualEvent(string method) + { + // VisualBasic does not allow to mark events as virtual (Overridable) + return Task.CompletedTask; + } + + public override async Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForInterfaceEvent(string method) + { + var source = $@"Imports NSubstitute +Imports NSubstitute.ReceivedExtensions +Imports System + +Namespace MyNamespace + Public Interface Foo + Event SomeEvent As Action + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + AddHandler {method}.SomeEvent, Sub() + End Sub + End Sub + End Class +End Namespace +"; + + await VerifyNoDiagnostic(source); + } + [CombinatoryData( "ReceivedExtensions.Received(substitute, Quantity.None())", "ReceivedExtensions.Received(substitute:= substitute, x:= Quantity.None())",