From ccc10d945b15bfd6b63c08434f07192ade8f9443 Mon Sep 17 00:00:00 2001 From: tpodolak Date: Sat, 14 Jul 2018 01:18:49 +0200 Subject: [PATCH] [GH-12] - fixes for VB implementation, additional test cases for VB --- .../AbstractReEntrantCallFinder.cs | 5 +- .../MetadataNames.cs | 1 + ...ReEntrantReturnsSetupDiagnosticVerifier.cs | 22 +- .../ReturnsAsExtensionMethodTests.cs | 253 +++++++++++++++++- .../ReturnsAsOrdinaryMethodTests.cs | 247 ++++++++++++++++- ...naryMethodWithGenericTypeSpecifiedTests.cs | 247 ++++++++++++++++- ...ReturnsForAnyArgsAsExtensionMethodTests.cs | 253 +++++++++++++++++- .../ReturnsForAnyArgsAsOrdinaryMethodTests.cs | 247 ++++++++++++++++- ...naryMethodWithGenericTypeSpecifiedTests.cs | 247 ++++++++++++++++- 9 files changed, 1494 insertions(+), 28 deletions(-) diff --git a/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractReEntrantCallFinder.cs b/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractReEntrantCallFinder.cs index 697b4e3f..5074c65c 100644 --- a/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractReEntrantCallFinder.cs +++ b/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractReEntrantCallFinder.cs @@ -12,7 +12,7 @@ internal abstract class AbstractReEntrantCallFinder { [MetadataNames.NSubstituteReturnsMethod] = MetadataNames.NSubstituteSubstituteExtensionsFullTypeName, [MetadataNames.NSubstituteReturnsForAnyArgsMethod] = MetadataNames.NSubstituteSubstituteExtensionsFullTypeName, - [MetadataNames.NSubstituteDoMethod] = "NSubstitute.Core.WhenCalled" + [MetadataNames.NSubstituteDoMethod] = MetadataNames.NSubstituteWhenCalledType }.ToImmutableDictionary(); public ImmutableList GetReEntrantCalls(SemanticModel semanticModel, SyntaxNode rootNode) @@ -53,7 +53,8 @@ protected bool IsReturnsLikeMethod(SemanticModel semanticModel, ISymbol symbol) } return symbol.ContainingAssembly?.Name.Equals(MetadataNames.NSubstituteAssemblyName, StringComparison.OrdinalIgnoreCase) == true && - symbol.ContainingType?.ConstructedFrom.ToString().Equals(containingType, StringComparison.OrdinalIgnoreCase) == true; + (symbol.ContainingType?.ToString().Equals(containingType, StringComparison.OrdinalIgnoreCase) == true || + (symbol.ContainingType?.ConstructedFrom.Name)?.Equals(containingType, StringComparison.OrdinalIgnoreCase) == true); } private static bool IsCalledViaDelegate(SemanticModel semanticModel, TypeInfo typeInfo) diff --git a/src/NSubstitute.Analyzers.Shared/MetadataNames.cs b/src/NSubstitute.Analyzers.Shared/MetadataNames.cs index f4e68153..e3431d7e 100644 --- a/src/NSubstitute.Analyzers.Shared/MetadataNames.cs +++ b/src/NSubstitute.Analyzers.Shared/MetadataNames.cs @@ -19,5 +19,6 @@ internal class MetadataNames public const string CastleDynamicProxyGenAssembly2Name = "DynamicProxyGenAssembly2"; public const string NSubstituteWhenMethod = "When"; public const string NSubstituteWhenForAnyArgsMethod = "WhenForAnyArgs"; + public const string NSubstituteWhenCalledType = "WhenCalled"; } } \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReEntrantReturnsSetupDiagnosticVerifier.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReEntrantReturnsSetupDiagnosticVerifier.cs index c024ab0d..d89d0287 100644 --- a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReEntrantReturnsSetupDiagnosticVerifier.cs +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReEntrantReturnsSetupDiagnosticVerifier.cs @@ -1,10 +1,28 @@ -using Microsoft.CodeAnalysis.Diagnostics; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; +using NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers; using NSubstitute.Analyzers.VisualBasic.DiagnosticAnalyzers; namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.ReEntrantReturnsSetupAnalyzerTests { - public abstract class ReEntrantReturnsSetupDiagnosticVerifier : VisualBasicDiagnosticVerifier + public abstract class ReEntrantReturnsSetupDiagnosticVerifier : VisualBasicDiagnosticVerifier, IReEntrantReturnsSetupDiagnosticVerifier { + public abstract Task ReturnsDiagnostic_WhenUsingReEntrantReturnsViaMethodCall(string reEntrantCall); + + public abstract Task ReturnsDiagnostic_WhenUsingReEntrantReturnsForAnyArgsViaMethodCall(string reEntrantCall); + + public abstract Task ReturnsDiagnostic_WhenUsingReEntrantWhenDo(string reEntrantCall); + + public abstract Task ReturnsDiagnostic_ForNestedReEntrantCall(); + + public abstract Task ReturnsDiagnostic_ForSpecificNestedReEntrantCall(); + + public abstract Task ReturnsNoDiagnostic_WhenRootCallCalledWithDelegate_AndReEntrantReturnsCallExists(string rootCall, string reEntrantCall); + + public abstract Task ReturnsNoDiagnostic_WhenRootCallCalledWithDelegate_AndReEntrantReturnsForAnyArgsCallExists(string rootCall, string reEntrantCall); + + public abstract Task ReturnsNoDiagnostic_WhenReEntrantSubstituteNotUsed(string firstReturn, string secondReturn); + protected override DiagnosticAnalyzer GetDiagnosticAnalyzer() { return new ReEntrantSetupAnalyzer(); diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsAsExtensionMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsAsExtensionMethodTests.cs index e062b6ef..dc2e4444 100644 --- a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsAsExtensionMethodTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsAsExtensionMethodTests.cs @@ -13,7 +13,7 @@ public class ReturnsAsExtensionMethodTests : ReEntrantReturnsSetupDiagnosticVeri [InlineData("substitute.Foo().Returns(1) \n\rOtherReturn()")] [InlineData("SubstituteExtensions.Returns(substitute.Foo(), 1)")] [InlineData("SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] - public async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsViaMethodCall(string reEntrantCall) + public override async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsViaMethodCall(string reEntrantCall) { var source = $@"Imports NSubstitute @@ -77,7 +77,7 @@ End Namespace [InlineData("OtherReturn()\r\n substitute.Foo().ReturnsForAnyArgs(1)")] [InlineData("SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] [InlineData("SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] - public async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsForAnyArgsViaMethodCall(string reEntrantCall) + public override async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsForAnyArgsViaMethodCall(string reEntrantCall) { var source = $@"Imports NSubstitute @@ -136,8 +136,69 @@ End Namespace await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic); } + [Theory] + [InlineData("substitute.[When](Function(x) x.Foo()).[Do](Function(callInfo) 1)")] + [InlineData("OtherReturn() \r\n substitute.[When](Function(x) x.Foo()).[Do](Function(callInfo) 1)")] + public override async Task ReturnsDiagnostic_WhenUsingReEntrantWhenDo(string reEntrantCall) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.Bar().Returns(ReturnThis(), OtherReturn()) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + {reEntrantCall} + Return 1 + End Function + End Class +End Namespace +"; + var firstArgumentDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.ReEntrantSubstituteCall, + Severity = DiagnosticSeverity.Warning, + Message = + "Returns() is set with a method that itself calls Do. This can cause problems with NSubstitute. Consider replacing with a lambda: Returns(Function(x) ReturnThis()).", + Locations = new[] + { + new DiagnosticResultLocation(15, 38) + } + }; + + var secondArgumentDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.ReEntrantSubstituteCall, + Severity = DiagnosticSeverity.Warning, + Message = + "Returns() is set with a method that itself calls Do. This can cause problems with NSubstitute. Consider replacing with a lambda: Returns(Function(x) OtherReturn()).", + Locations = new[] + { + new DiagnosticResultLocation(15, 52) + } + }; + + await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic); + } + [Fact] - public async Task ReturnsDiagnostic_ForNestedReEntrantCall() + public override async Task ReturnsDiagnostic_ForNestedReEntrantCall() { var source = @"Imports NSubstitute @@ -218,12 +279,196 @@ End Namespace await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic, nestedArgumentDiagnostic); } + [Fact] + public override async Task ReturnsDiagnostic_ForSpecificNestedReEntrantCall() + { + var source = @"Imports NSubstitute + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.Bar().Returns(Function(x) ReturnThis()) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + substitute.Foo().Returns(NestedReturnThis()) + Return 1 + End Function + + Private Function NestedReturnThis() As Integer + Return OtherNestedReturnThis() + End Function + + Private Function OtherNestedReturnThis() As Integer + Dim [sub] = Substitute.[For](Of IBar)() + [sub].Foo().Returns(1) + Return 1 + End Function + End Class +End Namespace +"; + + var firstArgumentDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.ReEntrantSubstituteCall, + Severity = DiagnosticSeverity.Warning, + Message = + "Returns() is set with a method that itself calls Returns. This can cause problems with NSubstitute. Consider replacing with a lambda: Returns(Function(x) NestedReturnThis()).", + Locations = new[] + { + new DiagnosticResultLocation(24, 38) + } + }; + + await VerifyDiagnostic(source, firstArgumentDiagnostic); + } + + [Theory] + [InlineData("MyMethod()", "substitute.Foo().Returns(1)")] + [InlineData("MyProperty", "substitute.Foo().Returns(1)")] + [InlineData("Function(x) ReturnThis()", "substitute.Foo().Returns(1)")] + [InlineData("Function(x) \r\n ReturnThis() \r\n End Function", "substitute.Foo().Returns(1)")] + [InlineData("MyMethod()", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("Function(x) \r\n ReturnThis() \r\n End Function", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("MyMethod()", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + [InlineData("Function(x) \r\n ReturnThis() \r\n End Function", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + public override async Task ReturnsNoDiagnostic_WhenRootCallCalledWithDelegate_AndReEntrantReturnsCallExists(string rootCall, string reEntrantCall) + { + var source = $@"Imports NSubstitute +Imports NSubstitute.Core +Imports System + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.Bar().Returns({rootCall}) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + {reEntrantCall} + Return 1 + End Function + + Private Function ReturnThisWithCallInfo(ByVal info As CallInfo) As Integer + Return OtherReturn() + End Function + + Private Function MyMethod() As Func(Of CallInfo, Integer) + Return AddressOf ReturnThisWithCallInfo + End Function + + Private ReadOnly Property MyProperty As Func(Of CallInfo, Integer) + Get + Return AddressOf ReturnThisWithCallInfo + End Get + End Property + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("MyMethod()", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("MyProperty", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("Function(x) ReturnThis()", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("Function(x) \r\n ReturnThis() \r\n End Function", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("MyMethod()", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("Function(x) \r\n ReturnThis() \r\n End Function", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("MyMethod()", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + [InlineData("Function(x) \r\n ReturnThis() \r\n End Function", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + public override async Task ReturnsNoDiagnostic_WhenRootCallCalledWithDelegate_AndReEntrantReturnsForAnyArgsCallExists(string rootCall, string reEntrantCall) + { + var source = $@"Imports NSubstitute +Imports NSubstitute.Core +Imports System + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.Bar().Returns({rootCall}) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + {reEntrantCall} + Return 1 + End Function + + Private Function ReturnThisWithCallInfo(ByVal info As CallInfo) As Integer + Return OtherReturn() + End Function + + Private Function MyMethod() As Func(Of CallInfo, Integer) + Return AddressOf ReturnThisWithCallInfo + End Function + + Private ReadOnly Property MyProperty As Func(Of CallInfo, Integer) + Get + Return AddressOf ReturnThisWithCallInfo + End Get + End Property + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + [Theory] [InlineData("ReturnThis()", "OtherReturn()")] [InlineData("ReturnThis", "OtherReturn")] [InlineData("1", "2")] [InlineData("Function(x) 1", "Function(x) 2")] - public async Task ReturnsNoDiagnostic_WhenReEntrantSubstituteNotUsed(string firstReturn, string secondReturn) + public override async Task ReturnsNoDiagnostic_WhenReEntrantSubstituteNotUsed(string firstReturn, string secondReturn) { var source = $@"Imports NSubstitute Imports NSubstitute.Core diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsAsOrdinaryMethodTests.cs index c7fb92e6..a7deb7bc 100644 --- a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsAsOrdinaryMethodTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsAsOrdinaryMethodTests.cs @@ -13,7 +13,7 @@ public class ReturnsAsOrdinaryMethodTests : ReEntrantReturnsSetupDiagnosticVerif [InlineData("substitute.Foo().Returns(1) \n\rOtherReturn()")] [InlineData("SubstituteExtensions.Returns(substitute.Foo(), 1)")] [InlineData("SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] - public async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsViaMethodCall(string reEntrantCall) + public override async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsViaMethodCall(string reEntrantCall) { var source = $@"Imports NSubstitute @@ -77,7 +77,7 @@ End Namespace [InlineData("OtherReturn()\r\n substitute.Foo().ReturnsForAnyArgs(1)")] [InlineData("SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] [InlineData("SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] - public async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsForAnyArgsViaMethodCall(string reEntrantCall) + public override async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsForAnyArgsViaMethodCall(string reEntrantCall) { var source = $@"Imports NSubstitute @@ -136,8 +136,69 @@ End Namespace await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic); } + [Theory] + [InlineData("substitute.[When](Function(x) x.Foo()).[Do](Function(callInfo) 1)")] + [InlineData("OtherReturn() \r\n substitute.[When](Function(x) x.Foo()).[Do](Function(callInfo) 1)")] + public override async Task ReturnsDiagnostic_WhenUsingReEntrantWhenDo(string reEntrantCall) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + SubstituteExtensions.Returns(substitute.Bar(), ReturnThis(), OtherReturn()) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + {reEntrantCall} + Return 1 + End Function + End Class +End Namespace +"; + var firstArgumentDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.ReEntrantSubstituteCall, + Severity = DiagnosticSeverity.Warning, + Message = + "Returns() is set with a method that itself calls Do. This can cause problems with NSubstitute. Consider replacing with a lambda: Returns(Function(x) ReturnThis()).", + Locations = new[] + { + new DiagnosticResultLocation(15, 60) + } + }; + + var secondArgumentDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.ReEntrantSubstituteCall, + Severity = DiagnosticSeverity.Warning, + Message = + "Returns() is set with a method that itself calls Do. This can cause problems with NSubstitute. Consider replacing with a lambda: Returns(Function(x) OtherReturn()).", + Locations = new[] + { + new DiagnosticResultLocation(15, 74) + } + }; + + await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic); + } + [Fact] - public async Task ReturnsDiagnostic_ForNestedReEntrantCall() + public override async Task ReturnsDiagnostic_ForNestedReEntrantCall() { var source = @"Imports NSubstitute @@ -218,12 +279,190 @@ End Namespace await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic, nestedArgumentDiagnostic); } + [Fact] + public override async Task ReturnsDiagnostic_ForSpecificNestedReEntrantCall() + { + var source = @"Imports NSubstitute + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + SubstituteExtensions.Returns(substitute.Bar(), Function(x) ReturnThis()) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + SubstituteExtensions.Returns(substitute.Foo(), NestedReturnThis()) + Return 1 + End Function + + Private Function NestedReturnThis() As Integer + Return OtherNestedReturnThis() + End Function + + Private Function OtherNestedReturnThis() As Integer + Dim [sub] = Substitute.[For](Of IBar)() + SubstituteExtensions.Returns([sub].Foo(), 1) + Return 1 + End Function + End Class +End Namespace +"; + + var firstArgumentDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.ReEntrantSubstituteCall, + Severity = DiagnosticSeverity.Warning, + Message = + "Returns() is set with a method that itself calls Returns. This can cause problems with NSubstitute. Consider replacing with a lambda: Returns(Function(x) NestedReturnThis()).", + Locations = new[] + { + new DiagnosticResultLocation(24, 60) + } + }; + + await VerifyDiagnostic(source, firstArgumentDiagnostic); + } + + [Theory] + [InlineData("MyMethod()", "substitute.Foo().Returns(1)")] + [InlineData("MyProperty", "substitute.Foo().Returns(1)")] + [InlineData("Function(x) ReturnThis()", "substitute.Foo().Returns(1)")] + [InlineData("MyMethod()", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("MyMethod()", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + public override async Task ReturnsNoDiagnostic_WhenRootCallCalledWithDelegate_AndReEntrantReturnsCallExists(string rootCall, string reEntrantCall) + { + var source = $@"Imports NSubstitute +Imports NSubstitute.Core +Imports System + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + SubstituteExtensions.Returns(substitute.Bar(), {rootCall}) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + {reEntrantCall} + Return 1 + End Function + + Private Function ReturnThisWithCallInfo(ByVal info As CallInfo) As Integer + Return OtherReturn() + End Function + + Private Function MyMethod() As Func(Of CallInfo, Integer) + Return AddressOf ReturnThisWithCallInfo + End Function + + Private ReadOnly Property MyProperty As Func(Of CallInfo, Integer) + Get + Return AddressOf ReturnThisWithCallInfo + End Get + End Property + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("MyMethod()", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("MyProperty", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("Function(x) ReturnThis()", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("MyMethod()", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("MyMethod()", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + public override async Task ReturnsNoDiagnostic_WhenRootCallCalledWithDelegate_AndReEntrantReturnsForAnyArgsCallExists(string rootCall, string reEntrantCall) + { + var source = $@"Imports NSubstitute +Imports NSubstitute.Core +Imports System + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + SubstituteExtensions.Returns(substitute.Bar(), {rootCall}) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + {reEntrantCall} + Return 1 + End Function + + Private Function ReturnThisWithCallInfo(ByVal info As CallInfo) As Integer + Return OtherReturn() + End Function + + Private Function MyMethod() As Func(Of CallInfo, Integer) + Return AddressOf ReturnThisWithCallInfo + End Function + + Private ReadOnly Property MyProperty As Func(Of CallInfo, Integer) + Get + Return AddressOf ReturnThisWithCallInfo + End Get + End Property + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + [Theory] [InlineData("ReturnThis()", "OtherReturn()")] [InlineData("ReturnThis", "OtherReturn")] [InlineData("1", "2")] [InlineData("Function(x) 1", "Function(x) 2")] - public async Task ReturnsNoDiagnostic_WhenReEntrantSubstituteNotUsed(string firstReturn, string secondReturn) + public override async Task ReturnsNoDiagnostic_WhenReEntrantSubstituteNotUsed(string firstReturn, string secondReturn) { var source = $@"Imports NSubstitute Imports NSubstitute.Core diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs index e71c12ac..f878c315 100644 --- a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs @@ -13,7 +13,7 @@ public class ReturnsAsOrdinaryMethodWithGenericTypeSpecifiedTests : ReEntrantRet [InlineData("substitute.Foo().Returns(1) \n\rOtherReturn()")] [InlineData("SubstituteExtensions.Returns(substitute.Foo(), 1)")] [InlineData("SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] - public async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsViaMethodCall(string reEntrantCall) + public override async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsViaMethodCall(string reEntrantCall) { var source = $@"Imports NSubstitute @@ -77,7 +77,7 @@ End Namespace [InlineData("OtherReturn()\r\n substitute.Foo().ReturnsForAnyArgs(1)")] [InlineData("SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] [InlineData("SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] - public async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsForAnyArgsViaMethodCall(string reEntrantCall) + public override async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsForAnyArgsViaMethodCall(string reEntrantCall) { var source = $@"Imports NSubstitute @@ -136,8 +136,69 @@ End Namespace await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic); } + [Theory] + [InlineData("substitute.[When](Function(x) x.Foo()).[Do](Function(callInfo) 1)")] + [InlineData("OtherReturn() \r\n substitute.[When](Function(x) x.Foo()).[Do](Function(callInfo) 1)")] + public override async Task ReturnsDiagnostic_WhenUsingReEntrantWhenDo(string reEntrantCall) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + SubstituteExtensions.Returns(Of Integer)(substitute.Bar(), ReturnThis(), OtherReturn()) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + {reEntrantCall} + Return 1 + End Function + End Class +End Namespace +"; + var firstArgumentDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.ReEntrantSubstituteCall, + Severity = DiagnosticSeverity.Warning, + Message = + "Returns() is set with a method that itself calls Do. This can cause problems with NSubstitute. Consider replacing with a lambda: Returns(Function(x) ReturnThis()).", + Locations = new[] + { + new DiagnosticResultLocation(15, 72) + } + }; + + var secondArgumentDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.ReEntrantSubstituteCall, + Severity = DiagnosticSeverity.Warning, + Message = + "Returns() is set with a method that itself calls Do. This can cause problems with NSubstitute. Consider replacing with a lambda: Returns(Function(x) OtherReturn()).", + Locations = new[] + { + new DiagnosticResultLocation(15, 86) + } + }; + + await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic); + } + [Fact] - public async Task ReturnsDiagnostic_ForNestedReEntrantCall() + public override async Task ReturnsDiagnostic_ForNestedReEntrantCall() { var source = @"Imports NSubstitute @@ -218,12 +279,190 @@ End Namespace await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic, nestedArgumentDiagnostic); } + [Fact] + public override async Task ReturnsDiagnostic_ForSpecificNestedReEntrantCall() + { + var source = @"Imports NSubstitute + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + SubstituteExtensions.Returns(Of Integer)(substitute.Bar(), Function(x) ReturnThis()) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), NestedReturnThis()) + Return 1 + End Function + + Private Function NestedReturnThis() As Integer + Return OtherNestedReturnThis() + End Function + + Private Function OtherNestedReturnThis() As Integer + Dim [sub] = Substitute.[For](Of IBar)() + SubstituteExtensions.Returns(Of Integer)([sub].Foo(), 1) + Return 1 + End Function + End Class +End Namespace +"; + + var firstArgumentDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.ReEntrantSubstituteCall, + Severity = DiagnosticSeverity.Warning, + Message = + "Returns() is set with a method that itself calls Returns. This can cause problems with NSubstitute. Consider replacing with a lambda: Returns(Function(x) NestedReturnThis()).", + Locations = new[] + { + new DiagnosticResultLocation(24, 72) + } + }; + + await VerifyDiagnostic(source, firstArgumentDiagnostic); + } + + [Theory] + [InlineData("MyMethod()", "substitute.Foo().Returns(1)")] + [InlineData("MyProperty", "substitute.Foo().Returns(1)")] + [InlineData("Function(x) ReturnThis()", "substitute.Foo().Returns(1)")] + [InlineData("MyMethod()", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("MyMethod()", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + public override async Task ReturnsNoDiagnostic_WhenRootCallCalledWithDelegate_AndReEntrantReturnsCallExists(string rootCall, string reEntrantCall) + { + var source = $@"Imports NSubstitute +Imports NSubstitute.Core +Imports System + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + SubstituteExtensions.Returns(Of Integer)(substitute.Bar(), {rootCall}) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + {reEntrantCall} + Return 1 + End Function + + Private Function ReturnThisWithCallInfo(ByVal info As CallInfo) As Integer + Return OtherReturn() + End Function + + Private Function MyMethod() As Func(Of CallInfo, Integer) + Return AddressOf ReturnThisWithCallInfo + End Function + + Private ReadOnly Property MyProperty As Func(Of CallInfo, Integer) + Get + Return AddressOf ReturnThisWithCallInfo + End Get + End Property + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("MyMethod()", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("MyProperty", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("Function(x) ReturnThis()", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("MyMethod()", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("MyMethod()", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + public override async Task ReturnsNoDiagnostic_WhenRootCallCalledWithDelegate_AndReEntrantReturnsForAnyArgsCallExists(string rootCall, string reEntrantCall) + { + var source = $@"Imports NSubstitute +Imports NSubstitute.Core +Imports System + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + SubstituteExtensions.Returns(Of Integer)(substitute.Bar(), {rootCall}) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + {reEntrantCall} + Return 1 + End Function + + Private Function ReturnThisWithCallInfo(ByVal info As CallInfo) As Integer + Return OtherReturn() + End Function + + Private Function MyMethod() As Func(Of CallInfo, Integer) + Return AddressOf ReturnThisWithCallInfo + End Function + + Private ReadOnly Property MyProperty As Func(Of CallInfo, Integer) + Get + Return AddressOf ReturnThisWithCallInfo + End Get + End Property + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + [Theory] [InlineData("ReturnThis()", "OtherReturn()")] [InlineData("ReturnThis", "OtherReturn")] [InlineData("1", "2")] [InlineData("Function(x) 1", "Function(x) 2")] - public async Task ReturnsNoDiagnostic_WhenReEntrantSubstituteNotUsed(string firstReturn, string secondReturn) + public override async Task ReturnsNoDiagnostic_WhenReEntrantSubstituteNotUsed(string firstReturn, string secondReturn) { var source = $@"Imports NSubstitute Imports NSubstitute.Core diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsForAnyArgsAsExtensionMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsForAnyArgsAsExtensionMethodTests.cs index 63ba1cea..e7c24dbb 100644 --- a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsForAnyArgsAsExtensionMethodTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsForAnyArgsAsExtensionMethodTests.cs @@ -13,7 +13,7 @@ public class ReturnsForAnyArgsAsExtensionMethodTests : ReEntrantReturnsSetupDiag [InlineData("substitute.Foo().Returns(1) \n\rOtherReturn()")] [InlineData("SubstituteExtensions.Returns(substitute.Foo(), 1)")] [InlineData("SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] - public async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsViaMethodCall(string reEntrantCall) + public override async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsViaMethodCall(string reEntrantCall) { var source = $@"Imports NSubstitute @@ -77,7 +77,7 @@ End Namespace [InlineData("OtherReturn()\r\n substitute.Foo().ReturnsForAnyArgs(1)")] [InlineData("SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] [InlineData("SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] - public async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsForAnyArgsViaMethodCall(string reEntrantCall) + public override async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsForAnyArgsViaMethodCall(string reEntrantCall) { var source = $@"Imports NSubstitute @@ -136,8 +136,69 @@ End Namespace await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic); } + [Theory] + [InlineData("substitute.[When](Function(x) x.Foo()).[Do](Function(callInfo) 1)")] + [InlineData("OtherReturn() \r\n substitute.[When](Function(x) x.Foo()).[Do](Function(callInfo) 1)")] + public override async Task ReturnsDiagnostic_WhenUsingReEntrantWhenDo(string reEntrantCall) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.Bar().ReturnsForAnyArgs(ReturnThis(), OtherReturn()) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + {reEntrantCall} + Return 1 + End Function + End Class +End Namespace +"; + var firstArgumentDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.ReEntrantSubstituteCall, + Severity = DiagnosticSeverity.Warning, + Message = + "ReturnsForAnyArgs() is set with a method that itself calls Do. This can cause problems with NSubstitute. Consider replacing with a lambda: ReturnsForAnyArgs(Function(x) ReturnThis()).", + Locations = new[] + { + new DiagnosticResultLocation(15, 48) + } + }; + + var secondArgumentDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.ReEntrantSubstituteCall, + Severity = DiagnosticSeverity.Warning, + Message = + "ReturnsForAnyArgs() is set with a method that itself calls Do. This can cause problems with NSubstitute. Consider replacing with a lambda: ReturnsForAnyArgs(Function(x) OtherReturn()).", + Locations = new[] + { + new DiagnosticResultLocation(15, 62) + } + }; + + await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic); + } + [Fact] - public async Task ReturnsDiagnostic_ForNestedReEntrantCall() + public override async Task ReturnsDiagnostic_ForNestedReEntrantCall() { var source = @"Imports NSubstitute @@ -218,12 +279,196 @@ End Namespace await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic, nestedArgumentDiagnostic); } + [Fact] + public override async Task ReturnsDiagnostic_ForSpecificNestedReEntrantCall() + { + var source = @"Imports NSubstitute + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.Bar().ReturnsForAnyArgs(Function(x) ReturnThis()) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + substitute.Foo().ReturnsForAnyArgs(NestedReturnThis()) + Return 1 + End Function + + Private Function NestedReturnThis() As Integer + Return OtherNestedReturnThis() + End Function + + Private Function OtherNestedReturnThis() As Integer + Dim [sub] = Substitute.[For](Of IBar)() + [sub].Foo().ReturnsForAnyArgs(1) + Return 1 + End Function + End Class +End Namespace +"; + + var firstArgumentDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.ReEntrantSubstituteCall, + Severity = DiagnosticSeverity.Warning, + Message = + "ReturnsForAnyArgs() is set with a method that itself calls ReturnsForAnyArgs. This can cause problems with NSubstitute. Consider replacing with a lambda: ReturnsForAnyArgs(Function(x) NestedReturnThis()).", + Locations = new[] + { + new DiagnosticResultLocation(24, 48) + } + }; + + await VerifyDiagnostic(source, firstArgumentDiagnostic); + } + + [Theory] + [InlineData("MyMethod()", "substitute.Foo().Returns(1)")] + [InlineData("MyProperty", "substitute.Foo().Returns(1)")] + [InlineData("Function(x) ReturnThis()", "substitute.Foo().Returns(1)")] + [InlineData("Function(x) \r\n ReturnThis() \r\n End Function", "substitute.Foo().Returns(1)")] + [InlineData("MyMethod()", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("Function(x) \r\n ReturnThis() \r\n End Function", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("MyMethod()", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + [InlineData("Function(x) \r\n ReturnThis() \r\n End Function", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + public override async Task ReturnsNoDiagnostic_WhenRootCallCalledWithDelegate_AndReEntrantReturnsCallExists(string rootCall, string reEntrantCall) + { + var source = $@"Imports NSubstitute +Imports NSubstitute.Core +Imports System + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.Bar().ReturnsForAnyArgs({rootCall}) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + {reEntrantCall} + Return 1 + End Function + + Private Function ReturnThisWithCallInfo(ByVal info As CallInfo) As Integer + Return OtherReturn() + End Function + + Private Function MyMethod() As Func(Of CallInfo, Integer) + Return AddressOf ReturnThisWithCallInfo + End Function + + Private ReadOnly Property MyProperty As Func(Of CallInfo, Integer) + Get + Return AddressOf ReturnThisWithCallInfo + End Get + End Property + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("MyMethod()", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("MyProperty", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("Function(x) ReturnThis()", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("Function(x) \r\n ReturnThis() \r\n End Function", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("MyMethod()", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("Function(x) \r\n ReturnThis() \r\n End Function", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("MyMethod()", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + [InlineData("Function(x) \r\n ReturnThis() \r\n End Function", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + public override async Task ReturnsNoDiagnostic_WhenRootCallCalledWithDelegate_AndReEntrantReturnsForAnyArgsCallExists(string rootCall, string reEntrantCall) + { + var source = $@"Imports NSubstitute +Imports NSubstitute.Core +Imports System + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.Bar().ReturnsForAnyArgs({rootCall}) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + {reEntrantCall} + Return 1 + End Function + + Private Function ReturnThisWithCallInfo(ByVal info As CallInfo) As Integer + Return OtherReturn() + End Function + + Private Function MyMethod() As Func(Of CallInfo, Integer) + Return AddressOf ReturnThisWithCallInfo + End Function + + Private ReadOnly Property MyProperty As Func(Of CallInfo, Integer) + Get + Return AddressOf ReturnThisWithCallInfo + End Get + End Property + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + [Theory] [InlineData("ReturnThis()", "OtherReturn()")] [InlineData("ReturnThis", "OtherReturn")] [InlineData("1", "2")] [InlineData("Function(x) 1", "Function(x) 2")] - public async Task ReturnsNoDiagnostic_WhenReEntrantSubstituteNotUsed(string firstReturn, string secondReturn) + public override async Task ReturnsNoDiagnostic_WhenReEntrantSubstituteNotUsed(string firstReturn, string secondReturn) { var source = $@"Imports NSubstitute Imports NSubstitute.Core diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsForAnyArgsAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsForAnyArgsAsOrdinaryMethodTests.cs index 48db5d49..7527b20f 100644 --- a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsForAnyArgsAsOrdinaryMethodTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsForAnyArgsAsOrdinaryMethodTests.cs @@ -13,7 +13,7 @@ public class ReturnsForAnyArgsAsOrdinaryMethodTests : ReEntrantReturnsSetupDiagn [InlineData("substitute.Foo().Returns(1) \n\rOtherReturn()")] [InlineData("SubstituteExtensions.Returns(substitute.Foo(), 1)")] [InlineData("SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] - public async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsViaMethodCall(string reEntrantCall) + public override async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsViaMethodCall(string reEntrantCall) { var source = $@"Imports NSubstitute @@ -77,7 +77,7 @@ End Namespace [InlineData("OtherReturn()\r\n substitute.Foo().ReturnsForAnyArgs(1)")] [InlineData("SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] [InlineData("SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] - public async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsForAnyArgsViaMethodCall(string reEntrantCall) + public override async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsForAnyArgsViaMethodCall(string reEntrantCall) { var source = $@"Imports NSubstitute @@ -136,8 +136,69 @@ End Namespace await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic); } + [Theory] + [InlineData("substitute.[When](Function(x) x.Foo()).[Do](Function(callInfo) 1)")] + [InlineData("OtherReturn() \r\n substitute.[When](Function(x) x.Foo()).[Do](Function(callInfo) 1)")] + public override async Task ReturnsDiagnostic_WhenUsingReEntrantWhenDo(string reEntrantCall) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + SubstituteExtensions.ReturnsForAnyArgs(substitute.Bar(), ReturnThis(), OtherReturn()) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + {reEntrantCall} + Return 1 + End Function + End Class +End Namespace +"; + var firstArgumentDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.ReEntrantSubstituteCall, + Severity = DiagnosticSeverity.Warning, + Message = + "ReturnsForAnyArgs() is set with a method that itself calls Do. This can cause problems with NSubstitute. Consider replacing with a lambda: ReturnsForAnyArgs(Function(x) ReturnThis()).", + Locations = new[] + { + new DiagnosticResultLocation(15, 70) + } + }; + + var secondArgumentDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.ReEntrantSubstituteCall, + Severity = DiagnosticSeverity.Warning, + Message = + "ReturnsForAnyArgs() is set with a method that itself calls Do. This can cause problems with NSubstitute. Consider replacing with a lambda: ReturnsForAnyArgs(Function(x) OtherReturn()).", + Locations = new[] + { + new DiagnosticResultLocation(15, 84) + } + }; + + await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic); + } + [Fact] - public async Task ReturnsDiagnostic_ForNestedReEntrantCall() + public override async Task ReturnsDiagnostic_ForNestedReEntrantCall() { var source = @"Imports NSubstitute @@ -218,12 +279,190 @@ End Namespace await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic, nestedArgumentDiagnostic); } + [Fact] + public override async Task ReturnsDiagnostic_ForSpecificNestedReEntrantCall() + { + var source = @"Imports NSubstitute + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + SubstituteExtensions.ReturnsForAnyArgs(substitute.Bar(), Function(x) ReturnThis()) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), NestedReturnThis()) + Return 1 + End Function + + Private Function NestedReturnThis() As Integer + Return OtherNestedReturnThis() + End Function + + Private Function OtherNestedReturnThis() As Integer + Dim [sub] = Substitute.[For](Of IBar)() + SubstituteExtensions.ReturnsForAnyArgs([sub].Foo(), 1) + Return 1 + End Function + End Class +End Namespace +"; + + var firstArgumentDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.ReEntrantSubstituteCall, + Severity = DiagnosticSeverity.Warning, + Message = + "ReturnsForAnyArgs() is set with a method that itself calls ReturnsForAnyArgs. This can cause problems with NSubstitute. Consider replacing with a lambda: ReturnsForAnyArgs(Function(x) NestedReturnThis()).", + Locations = new[] + { + new DiagnosticResultLocation(24, 70) + } + }; + + await VerifyDiagnostic(source, firstArgumentDiagnostic); + } + + [Theory] + [InlineData("MyMethod()", "substitute.Foo().Returns(1)")] + [InlineData("MyProperty", "substitute.Foo().Returns(1)")] + [InlineData("Function(x) ReturnThis()", "substitute.Foo().Returns(1)")] + [InlineData("MyMethod()", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("MyMethod()", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + public override async Task ReturnsNoDiagnostic_WhenRootCallCalledWithDelegate_AndReEntrantReturnsCallExists(string rootCall, string reEntrantCall) + { + var source = $@"Imports NSubstitute +Imports NSubstitute.Core +Imports System + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + SubstituteExtensions.ReturnsForAnyArgs(substitute.Bar(), {rootCall}) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + {reEntrantCall} + Return 1 + End Function + + Private Function ReturnThisWithCallInfo(ByVal info As CallInfo) As Integer + Return OtherReturn() + End Function + + Private Function MyMethod() As Func(Of CallInfo, Integer) + Return AddressOf ReturnThisWithCallInfo + End Function + + Private ReadOnly Property MyProperty As Func(Of CallInfo, Integer) + Get + Return AddressOf ReturnThisWithCallInfo + End Get + End Property + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("MyMethod()", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("MyProperty", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("Function(x) ReturnThis()", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("MyMethod()", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("MyMethod()", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + public override async Task ReturnsNoDiagnostic_WhenRootCallCalledWithDelegate_AndReEntrantReturnsForAnyArgsCallExists(string rootCall, string reEntrantCall) + { + var source = $@"Imports NSubstitute +Imports NSubstitute.Core +Imports System + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + SubstituteExtensions.ReturnsForAnyArgs(substitute.Bar(), {rootCall}) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + {reEntrantCall} + Return 1 + End Function + + Private Function ReturnThisWithCallInfo(ByVal info As CallInfo) As Integer + Return OtherReturn() + End Function + + Private Function MyMethod() As Func(Of CallInfo, Integer) + Return AddressOf ReturnThisWithCallInfo + End Function + + Private ReadOnly Property MyProperty As Func(Of CallInfo, Integer) + Get + Return AddressOf ReturnThisWithCallInfo + End Get + End Property + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + [Theory] [InlineData("ReturnThis()", "OtherReturn()")] [InlineData("ReturnThis", "OtherReturn")] [InlineData("1", "2")] [InlineData("Function(x) 1", "Function(x) 2")] - public async Task ReturnsNoDiagnostic_WhenReEntrantSubstituteNotUsed(string firstReturn, string secondReturn) + public override async Task ReturnsNoDiagnostic_WhenReEntrantSubstituteNotUsed(string firstReturn, string secondReturn) { var source = $@"Imports NSubstitute Imports NSubstitute.Core diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsForAnyArgsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsForAnyArgsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs index e47d2abb..f6935520 100644 --- a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsForAnyArgsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ReEntrantReturnsSetupAnalyzerTests/ReturnsForAnyArgsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs @@ -13,7 +13,7 @@ public class ReturnsForAnyArgsAsOrdinaryMethodWithGenericTypeSpecifiedTests : Re [InlineData("substitute.Foo().Returns(1) \n\rOtherReturn()")] [InlineData("SubstituteExtensions.Returns(substitute.Foo(), 1)")] [InlineData("SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] - public async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsViaMethodCall(string reEntrantCall) + public override async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsViaMethodCall(string reEntrantCall) { var source = $@"Imports NSubstitute @@ -77,7 +77,7 @@ End Namespace [InlineData("OtherReturn()\r\n substitute.Foo().ReturnsForAnyArgs(1)")] [InlineData("SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] [InlineData("SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] - public async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsForAnyArgsViaMethodCall(string reEntrantCall) + public override async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsForAnyArgsViaMethodCall(string reEntrantCall) { var source = $@"Imports NSubstitute @@ -136,8 +136,69 @@ End Namespace await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic); } + [Theory] + [InlineData("substitute.[When](Function(x) x.Foo()).[Do](Function(callInfo) 1)")] + [InlineData("OtherReturn() \r\n substitute.[When](Function(x) x.Foo()).[Do](Function(callInfo) 1)")] + public override async Task ReturnsDiagnostic_WhenUsingReEntrantWhenDo(string reEntrantCall) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Bar(), ReturnThis(), OtherReturn()) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + {reEntrantCall} + Return 1 + End Function + End Class +End Namespace +"; + var firstArgumentDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.ReEntrantSubstituteCall, + Severity = DiagnosticSeverity.Warning, + Message = + "ReturnsForAnyArgs() is set with a method that itself calls Do. This can cause problems with NSubstitute. Consider replacing with a lambda: ReturnsForAnyArgs(Function(x) ReturnThis()).", + Locations = new[] + { + new DiagnosticResultLocation(15, 82) + } + }; + + var secondArgumentDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.ReEntrantSubstituteCall, + Severity = DiagnosticSeverity.Warning, + Message = + "ReturnsForAnyArgs() is set with a method that itself calls Do. This can cause problems with NSubstitute. Consider replacing with a lambda: ReturnsForAnyArgs(Function(x) OtherReturn()).", + Locations = new[] + { + new DiagnosticResultLocation(15, 96) + } + }; + + await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic); + } + [Fact] - public async Task ReturnsDiagnostic_ForNestedReEntrantCall() + public override async Task ReturnsDiagnostic_ForNestedReEntrantCall() { var source = @"Imports NSubstitute @@ -218,12 +279,190 @@ End Namespace await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic, nestedArgumentDiagnostic); } + [Fact] + public override async Task ReturnsDiagnostic_ForSpecificNestedReEntrantCall() + { + var source = @"Imports NSubstitute + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Bar(), Function(x) ReturnThis()) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), NestedReturnThis()) + Return 1 + End Function + + Private Function NestedReturnThis() As Integer + Return OtherNestedReturnThis() + End Function + + Private Function OtherNestedReturnThis() As Integer + Dim [sub] = Substitute.[For](Of IBar)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)([sub].Foo(), 1) + Return 1 + End Function + End Class +End Namespace +"; + + var firstArgumentDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.ReEntrantSubstituteCall, + Severity = DiagnosticSeverity.Warning, + Message = + "ReturnsForAnyArgs() is set with a method that itself calls ReturnsForAnyArgs. This can cause problems with NSubstitute. Consider replacing with a lambda: ReturnsForAnyArgs(Function(x) NestedReturnThis()).", + Locations = new[] + { + new DiagnosticResultLocation(24, 82) + } + }; + + await VerifyDiagnostic(source, firstArgumentDiagnostic); + } + + [Theory] + [InlineData("MyMethod()", "substitute.Foo().Returns(1)")] + [InlineData("MyProperty", "substitute.Foo().Returns(1)")] + [InlineData("Function(x) ReturnThis()", "substitute.Foo().Returns(1)")] + [InlineData("MyMethod()", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.Returns(substitute.Foo(), 1)")] + [InlineData("MyMethod()", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")] + public override async Task ReturnsNoDiagnostic_WhenRootCallCalledWithDelegate_AndReEntrantReturnsCallExists(string rootCall, string reEntrantCall) + { + var source = $@"Imports NSubstitute +Imports NSubstitute.Core +Imports System + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Bar(), {rootCall}) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + {reEntrantCall} + Return 1 + End Function + + Private Function ReturnThisWithCallInfo(ByVal info As CallInfo) As Integer + Return OtherReturn() + End Function + + Private Function MyMethod() As Func(Of CallInfo, Integer) + Return AddressOf ReturnThisWithCallInfo + End Function + + Private ReadOnly Property MyProperty As Func(Of CallInfo, Integer) + Get + Return AddressOf ReturnThisWithCallInfo + End Get + End Property + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("MyMethod()", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("MyProperty", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("Function(x) ReturnThis()", "substitute.Foo().ReturnsForAnyArgs(1)")] + [InlineData("MyMethod()", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")] + [InlineData("MyMethod()", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + [InlineData("MyProperty", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + [InlineData("Function(x) ReturnThis()", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")] + public override async Task ReturnsNoDiagnostic_WhenRootCallCalledWithDelegate_AndReEntrantReturnsForAnyArgsCallExists(string rootCall, string reEntrantCall) + { + var source = $@"Imports NSubstitute +Imports NSubstitute.Core +Imports System + +Namespace MyNamespace + Interface IFoo + Function Bar() As Integer + End Interface + + Interface IBar + Function Foo() As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Bar(), {rootCall}) + End Sub + + Private Function ReturnThis() As Integer + Return OtherReturn() + End Function + + Private Function OtherReturn() As Integer + Dim substitute = NSubstitute.Substitute.[For](Of IBar)() + {reEntrantCall} + Return 1 + End Function + + Private Function ReturnThisWithCallInfo(ByVal info As CallInfo) As Integer + Return OtherReturn() + End Function + + Private Function MyMethod() As Func(Of CallInfo, Integer) + Return AddressOf ReturnThisWithCallInfo + End Function + + Private ReadOnly Property MyProperty As Func(Of CallInfo, Integer) + Get + Return AddressOf ReturnThisWithCallInfo + End Get + End Property + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + [Theory] [InlineData("ReturnThis()", "OtherReturn()")] [InlineData("ReturnThis", "OtherReturn")] [InlineData("1", "2")] [InlineData("Function(x) 1", "Function(x) 2")] - public async Task ReturnsNoDiagnostic_WhenReEntrantSubstituteNotUsed(string firstReturn, string secondReturn) + public override async Task ReturnsNoDiagnostic_WhenReEntrantSubstituteNotUsed(string firstReturn, string secondReturn) { var source = $@"Imports NSubstitute Imports NSubstitute.Core