diff --git a/src/NSubstitute.Analyzers.VisualBasic/DiagnosticAnalyzers/CallInfoAnalyzer.cs b/src/NSubstitute.Analyzers.VisualBasic/DiagnosticAnalyzers/CallInfoAnalyzer.cs index b4de1a56..3571c35c 100644 --- a/src/NSubstitute.Analyzers.VisualBasic/DiagnosticAnalyzers/CallInfoAnalyzer.cs +++ b/src/NSubstitute.Analyzers.VisualBasic/DiagnosticAnalyzers/CallInfoAnalyzer.cs @@ -20,7 +20,15 @@ public CallInfoAnalyzer() protected override SyntaxNode GetSubstituteCall(IMethodSymbol methodSymbol, InvocationExpressionSyntax invocationExpressionSyntax) { - return invocationExpressionSyntax.Expression.DescendantNodes().First(); + switch (methodSymbol.MethodKind) + { + case MethodKind.ReducedExtension: + return invocationExpressionSyntax.Expression.DescendantNodes().First(); + case MethodKind.Ordinary: + return invocationExpressionSyntax.ArgumentList.Arguments.First().GetExpression(); + default: + return null; + } } protected override IEnumerable GetArgumentExpressions(InvocationExpressionSyntax invocationExpressionSyntax) diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ReturnsAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ReturnsAsOrdinaryMethodTests.cs new file mode 100644 index 00000000..8cfdc25e --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ReturnsAsOrdinaryMethodTests.cs @@ -0,0 +1,610 @@ +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using NSubstitute.Analyzers.Shared; +using NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers; +using Xunit; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.CallInfoAnalyzerTests +{ + public class ReturnsAsOrdinaryMethodTests : CallInfoDiagnosticVerifier + { + [Theory] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)", 15, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "Dim x = callInfo(1)", 15, 40)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo(1) = 1", 15, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)", 15, 40)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.Args()(1) = 1", 15, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.ArgTypes()(1) = GetType(Integer)", 15, 32)] + [InlineData("substitute.Barr", "callInfo.ArgAt(Of Integer)(1)", 15, 32)] + [InlineData("substitute.Barr", "Dim x = callInfo(1)", 15, 40)] + [InlineData("substitute.Barr", "callInfo(1) = 1", 15, 32)] + [InlineData("substitute.Barr", "Dim x = callInfo.Args()(1)", 15, 40)] + [InlineData("substitute.Barr", "callInfo.Args()(1) = 1", 15, 32)] + [InlineData("substitute.Barr", "callInfo.ArgTypes()(1) = GetType(Integer)", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "Dim x = callInfo(1)", 15, 40)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo(1) = 1", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)", 15, 40)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.Args()(1) = 1", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.ArgTypes()(1) = GetType(Integer)", 15, 32)] + public override async Task ReportsDiagnostic_WhenAccessingArgumentOutOfBounds(string call, string argAccess, int expectedLine, int expectedColumn) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentOutOfRange, + Severity = DiagnosticSeverity.Warning, + Message = "There is no argument at position 1", + Locations = new[] + { + new DiagnosticResultLocation(expectedLine, expectedColumn) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(1)")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentWithinBounds(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "callInfo.ArgAt(Of Bar)(1)", 17, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo(1), Bar)", 17, 46)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo(1), Bar)", 17, 48)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo(1), Bar)", 17, 51)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo.Args()(1), Bar)", 17, 46)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo.Args()(1), Bar)", 17, 48)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo.Args()(1), Bar)", 17, 51)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "callInfo.ArgAt(Of Bar)(1)", 17, 32)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo(1), Bar)", 17, 46)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo(1), Bar)", 17, 48)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo(1), Bar)", 17, 51)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo.Args()(1), Bar)", 17, 46)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo.Args()(1), Bar)", 17, 48)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo.Args()(1), Bar)", 17, 51)] + public override async Task ReportsDiagnostic_WhenConvertingTypeToUnsupportedType(string call, string argAccess, int expectedLine, int expectedColumn) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoCouldNotConvertParameterAtPosition, + Severity = DiagnosticSeverity.Warning, + Message = "Couldn't convert parameter at position 1 to type MyNamespace.Bar.", + Locations = new[] + { + new DiagnosticResultLocation(expectedLine, expectedColumn) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.ArgAt(Of Bar)(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.Args()(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.ArgAt(Of Bar)(0)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.Args()(0), Bar)")] + public override async Task ReportsNoDiagnostic_WhenConvertingTypeToSupportedType(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes()(0), Object)")] + public override async Task ReportsNoDiagnostic_WhenCastingElementsFromArgTypes(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.ArgTypes()(0) = GetType(Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.Args()(0) = 1D")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.ArgTypes()(0) = GetType(Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.Args()(0) = 1D")] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToNotRefNorOutArgumentViaIndirectCall(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())")] + [InlineData("substitute.Barr")] + [InlineData("substitute(Arg.Any(Of Integer)())")] + public override async Task ReportsDiagnostic_WhenAccessingArgumentByTypeNotInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns({call}, Function(callInfo) + callInfo.Arg(Of Double)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoCouldNotFindArgumentToThisCall, + Severity = DiagnosticSeverity.Warning, + Message = "Can not find an argument of type Double to this call.", + Locations = new[] + { + new DiagnosticResultLocation(16, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())")] + [InlineData("substitute(Arg.Any(Of Integer)())")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeInInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns({call}, Function(callInfo) + callInfo.Arg(Of Integer)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())")] + public override async Task ReportsDiagnostic_WhenAccessingArgumentByTypeMultipleTimesInInvocation(string call) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Integer) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns({call}, Function(callInfo) + callInfo.Arg(Of Integer)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoMoreThanOneArgumentOfType, + Severity = DiagnosticSeverity.Warning, + Message = "There is more than one argument of type Integer to this call.", + Locations = new[] + { + new DiagnosticResultLocation(13, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeMultipleDifferentTypesInInvocation(string call) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns({call}, Function(callInfo) + callInfo.Arg(Of Integer)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + public override async Task ReportsDiagnostic_WhenAssigningValueToNotOutNorRefArgument(string call) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns({call}, Function(callInfo) + callInfo(1) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentIsNotOutOrRef, + Severity = DiagnosticSeverity.Warning, + Message = "Could not set argument 1 (Double) as it is not an out or ref argument.", + Locations = new[] + { + new DiagnosticResultLocation(13, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToRefArgument() + { + var source = @"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToOutArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Fact] + public override async Task ReportsDiagnostic_WhenAssigningValueToOutOfBoundsArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(substitute.Bar(value), Function(callInfo) + callInfo(1) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentOutOfRange, + Severity = DiagnosticSeverity.Warning, + Message = "There is no argument at position 1", + Locations = new[] + { + new DiagnosticResultLocation(14, 47) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsDiagnostic_WhenAssigningWrongTypeToArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Decimal) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Decimal = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentSetWithIncompatibleValue, + Severity = DiagnosticSeverity.Warning, + Message = "Could not set value of type Integer to argument 0 (Decimal) because the types are incompatible.", + Locations = new[] + { + new DiagnosticResultLocation(14, 47) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningProperTypeToArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Decimal) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Decimal = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1D + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ReturnsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ReturnsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs new file mode 100644 index 00000000..0dbcccbf --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ReturnsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs @@ -0,0 +1,610 @@ +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using NSubstitute.Analyzers.Shared; +using NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers; +using Xunit; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.CallInfoAnalyzerTests +{ + public class ReturnsAsOrdinaryMethodWithGenericTypeSpecifiedTests : CallInfoDiagnosticVerifier + { + [Theory] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)", 15, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "Dim x = callInfo(1)", 15, 40)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo(1) = 1", 15, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)", 15, 40)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.Args()(1) = 1", 15, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.ArgTypes()(1) = GetType(Integer)", 15, 32)] + [InlineData("substitute.Barr", "callInfo.ArgAt(Of Integer)(1)", 15, 32)] + [InlineData("substitute.Barr", "Dim x = callInfo(1)", 15, 40)] + [InlineData("substitute.Barr", "callInfo(1) = 1", 15, 32)] + [InlineData("substitute.Barr", "Dim x = callInfo.Args()(1)", 15, 40)] + [InlineData("substitute.Barr", "callInfo.Args()(1) = 1", 15, 32)] + [InlineData("substitute.Barr", "callInfo.ArgTypes()(1) = GetType(Integer)", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "Dim x = callInfo(1)", 15, 40)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo(1) = 1", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)", 15, 40)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.Args()(1) = 1", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.ArgTypes()(1) = GetType(Integer)", 15, 32)] + public override async Task ReportsDiagnostic_WhenAccessingArgumentOutOfBounds(string call, string argAccess, int expectedLine, int expectedColumn) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(Of Integer)({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentOutOfRange, + Severity = DiagnosticSeverity.Warning, + Message = "There is no argument at position 1", + Locations = new[] + { + new DiagnosticResultLocation(expectedLine, expectedColumn) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(1)")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentWithinBounds(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(Of Integer)({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "callInfo.ArgAt(Of Bar)(1)", 17, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo(1), Bar)", 17, 46)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo(1), Bar)", 17, 48)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo(1), Bar)", 17, 51)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo.Args()(1), Bar)", 17, 46)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo.Args()(1), Bar)", 17, 48)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo.Args()(1), Bar)", 17, 51)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "callInfo.ArgAt(Of Bar)(1)", 17, 32)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo(1), Bar)", 17, 46)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo(1), Bar)", 17, 48)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo(1), Bar)", 17, 51)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo.Args()(1), Bar)", 17, 46)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo.Args()(1), Bar)", 17, 48)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo.Args()(1), Bar)", 17, 51)] + public override async Task ReportsDiagnostic_WhenConvertingTypeToUnsupportedType(string call, string argAccess, int expectedLine, int expectedColumn) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(Of Integer)({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoCouldNotConvertParameterAtPosition, + Severity = DiagnosticSeverity.Warning, + Message = "Couldn't convert parameter at position 1 to type MyNamespace.Bar.", + Locations = new[] + { + new DiagnosticResultLocation(expectedLine, expectedColumn) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.ArgAt(Of Bar)(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.Args()(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.ArgAt(Of Bar)(0)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.Args()(0), Bar)")] + public override async Task ReportsNoDiagnostic_WhenConvertingTypeToSupportedType(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(Of Integer)({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes()(0), Object)")] + public override async Task ReportsNoDiagnostic_WhenCastingElementsFromArgTypes(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(Of Integer)({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.ArgTypes()(0) = GetType(Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.Args()(0) = 1D")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.ArgTypes()(0) = GetType(Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.Args()(0) = 1D")] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToNotRefNorOutArgumentViaIndirectCall(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(Of Integer)({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())")] + [InlineData("substitute.Barr")] + [InlineData("substitute(Arg.Any(Of Integer)())")] + public override async Task ReportsDiagnostic_WhenAccessingArgumentByTypeNotInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(Of Integer)({call}, Function(callInfo) + callInfo.Arg(Of Double)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoCouldNotFindArgumentToThisCall, + Severity = DiagnosticSeverity.Warning, + Message = "Can not find an argument of type Double to this call.", + Locations = new[] + { + new DiagnosticResultLocation(16, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())")] + [InlineData("substitute(Arg.Any(Of Integer)())")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeInInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(Of Integer)({call}, Function(callInfo) + callInfo.Arg(Of Integer)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())")] + public override async Task ReportsDiagnostic_WhenAccessingArgumentByTypeMultipleTimesInInvocation(string call) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Integer) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(Of Integer)({call}, Function(callInfo) + callInfo.Arg(Of Integer)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoMoreThanOneArgumentOfType, + Severity = DiagnosticSeverity.Warning, + Message = "There is more than one argument of type Integer to this call.", + Locations = new[] + { + new DiagnosticResultLocation(13, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeMultipleDifferentTypesInInvocation(string call) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(Of Integer)({call}, Function(callInfo) + callInfo.Arg(Of Integer)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + public override async Task ReportsDiagnostic_WhenAssigningValueToNotOutNorRefArgument(string call) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(Of Integer)({call}, Function(callInfo) + callInfo(1) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentIsNotOutOrRef, + Severity = DiagnosticSeverity.Warning, + Message = "Could not set argument 1 (Double) as it is not an out or ref argument.", + Locations = new[] + { + new DiagnosticResultLocation(13, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToRefArgument() + { + var source = @"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(Of Integer)(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToOutArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(Of Integer)(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Fact] + public override async Task ReportsDiagnostic_WhenAssigningValueToOutOfBoundsArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(Of Integer)(substitute.Bar(value), Function(callInfo) + callInfo(1) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentOutOfRange, + Severity = DiagnosticSeverity.Warning, + Message = "There is no argument at position 1", + Locations = new[] + { + new DiagnosticResultLocation(14, 47) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsDiagnostic_WhenAssigningWrongTypeToArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Decimal) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Decimal = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(Of Integer)(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentSetWithIncompatibleValue, + Severity = DiagnosticSeverity.Warning, + Message = "Could not set value of type Integer to argument 0 (Decimal) because the types are incompatible.", + Locations = new[] + { + new DiagnosticResultLocation(14, 47) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningProperTypeToArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Decimal) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Decimal = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.Returns(Of Integer)(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1D + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ReturnsForAnyArgsAsExtensionMethodsTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ReturnsForAnyArgsAsExtensionMethodsTests.cs new file mode 100644 index 00000000..5e053f95 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ReturnsForAnyArgsAsExtensionMethodsTests.cs @@ -0,0 +1,610 @@ +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using NSubstitute.Analyzers.Shared; +using NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers; +using Xunit; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.CallInfoAnalyzerTests +{ + public class ReturnsForAnyArgsAsExtensionMethodsTests : CallInfoDiagnosticVerifier + { + [Theory] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)", 15, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "Dim x = callInfo(1)", 15, 40)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo(1) = 1", 15, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)", 15, 40)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.Args()(1) = 1", 15, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.ArgTypes()(1) = GetType(Integer)", 15, 32)] + [InlineData("substitute.Barr", "callInfo.ArgAt(Of Integer)(1)", 15, 32)] + [InlineData("substitute.Barr", "Dim x = callInfo(1)", 15, 40)] + [InlineData("substitute.Barr", "callInfo(1) = 1", 15, 32)] + [InlineData("substitute.Barr", "Dim x = callInfo.Args()(1)", 15, 40)] + [InlineData("substitute.Barr", "callInfo.Args()(1) = 1", 15, 32)] + [InlineData("substitute.Barr", "callInfo.ArgTypes()(1) = GetType(Integer)", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "Dim x = callInfo(1)", 15, 40)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo(1) = 1", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)", 15, 40)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.Args()(1) = 1", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.ArgTypes()(1) = GetType(Integer)", 15, 32)] + public override async Task ReportsDiagnostic_WhenAccessingArgumentOutOfBounds(string call, string argAccess, int expectedLine, int expectedColumn) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ReturnsForAnyArgs(Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentOutOfRange, + Severity = DiagnosticSeverity.Warning, + Message = "There is no argument at position 1", + Locations = new[] + { + new DiagnosticResultLocation(expectedLine, expectedColumn) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(1)")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentWithinBounds(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ReturnsForAnyArgs(Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "callInfo.ArgAt(Of Bar)(1)", 17, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo(1), Bar)", 17, 46)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo(1), Bar)", 17, 48)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo(1), Bar)", 17, 51)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo.Args()(1), Bar)", 17, 46)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo.Args()(1), Bar)", 17, 48)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo.Args()(1), Bar)", 17, 51)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "callInfo.ArgAt(Of Bar)(1)", 17, 32)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo(1), Bar)", 17, 46)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo(1), Bar)", 17, 48)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo(1), Bar)", 17, 51)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo.Args()(1), Bar)", 17, 46)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo.Args()(1), Bar)", 17, 48)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo.Args()(1), Bar)", 17, 51)] + public override async Task ReportsDiagnostic_WhenConvertingTypeToUnsupportedType(string call, string argAccess, int expectedLine, int expectedColumn) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ReturnsForAnyArgs(Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoCouldNotConvertParameterAtPosition, + Severity = DiagnosticSeverity.Warning, + Message = "Couldn't convert parameter at position 1 to type MyNamespace.Bar.", + Locations = new[] + { + new DiagnosticResultLocation(expectedLine, expectedColumn) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.ArgAt(Of Bar)(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.Args()(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.ArgAt(Of Bar)(0)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.Args()(0), Bar)")] + public override async Task ReportsNoDiagnostic_WhenConvertingTypeToSupportedType(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ReturnsForAnyArgs(Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes()(0), Object)")] + public override async Task ReportsNoDiagnostic_WhenCastingElementsFromArgTypes(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ReturnsForAnyArgs(Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.ArgTypes()(0) = GetType(Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.Args()(0) = 1D")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.ArgTypes()(0) = GetType(Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.Args()(0) = 1D")] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToNotRefNorOutArgumentViaIndirectCall(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ReturnsForAnyArgs(Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())")] + [InlineData("substitute.Barr")] + [InlineData("substitute(Arg.Any(Of Integer)())")] + public override async Task ReportsDiagnostic_WhenAccessingArgumentByTypeNotInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ReturnsForAnyArgs(Function(callInfo) + callInfo.Arg(Of Double)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoCouldNotFindArgumentToThisCall, + Severity = DiagnosticSeverity.Warning, + Message = "Can not find an argument of type Double to this call.", + Locations = new[] + { + new DiagnosticResultLocation(16, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())")] + [InlineData("substitute(Arg.Any(Of Integer)())")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeInInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ReturnsForAnyArgs(Function(callInfo) + callInfo.Arg(Of Integer)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())")] + public override async Task ReportsDiagnostic_WhenAccessingArgumentByTypeMultipleTimesInInvocation(string call) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Integer) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ReturnsForAnyArgs(Function(callInfo) + callInfo.Arg(Of Integer)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoMoreThanOneArgumentOfType, + Severity = DiagnosticSeverity.Warning, + Message = "There is more than one argument of type Integer to this call.", + Locations = new[] + { + new DiagnosticResultLocation(13, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeMultipleDifferentTypesInInvocation(string call) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ReturnsForAnyArgs(Function(callInfo) + callInfo.Arg(Of Integer)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + public override async Task ReportsDiagnostic_WhenAssigningValueToNotOutNorRefArgument(string call) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ReturnsForAnyArgs(Function(callInfo) + callInfo(1) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentIsNotOutOrRef, + Severity = DiagnosticSeverity.Warning, + Message = "Could not set argument 1 (Double) as it is not an out or ref argument.", + Locations = new[] + { + new DiagnosticResultLocation(13, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToRefArgument() + { + var source = @"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar(value).ReturnsForAnyArgs(Function(callInfo) + callInfo(0) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToOutArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar(value).ReturnsForAnyArgs(Function(callInfo) + callInfo(0) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Fact] + public override async Task ReportsDiagnostic_WhenAssigningValueToOutOfBoundsArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar(value).ReturnsForAnyArgs(Function(callInfo) + callInfo(1) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentOutOfRange, + Severity = DiagnosticSeverity.Warning, + Message = "There is no argument at position 1", + Locations = new[] + { + new DiagnosticResultLocation(14, 47) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsDiagnostic_WhenAssigningWrongTypeToArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Decimal) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Decimal = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar(value).ReturnsForAnyArgs(Function(callInfo) + callInfo(0) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentSetWithIncompatibleValue, + Severity = DiagnosticSeverity.Warning, + Message = "Could not set value of type Integer to argument 0 (Decimal) because the types are incompatible.", + Locations = new[] + { + new DiagnosticResultLocation(14, 47) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningProperTypeToArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Decimal) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Decimal = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar(value).ReturnsForAnyArgs(Function(callInfo) + callInfo(0) = 1D + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ReturnsForAnyArgsAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ReturnsForAnyArgsAsOrdinaryMethodTests.cs new file mode 100644 index 00000000..1ea872fb --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ReturnsForAnyArgsAsOrdinaryMethodTests.cs @@ -0,0 +1,610 @@ +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using NSubstitute.Analyzers.Shared; +using NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers; +using Xunit; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.CallInfoAnalyzerTests +{ + public class ReturnsForAnyArgsAsOrdinaryMethodTests : CallInfoDiagnosticVerifier + { + [Theory] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)", 15, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "Dim x = callInfo(1)", 15, 40)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo(1) = 1", 15, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)", 15, 40)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.Args()(1) = 1", 15, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.ArgTypes()(1) = GetType(Integer)", 15, 32)] + [InlineData("substitute.Barr", "callInfo.ArgAt(Of Integer)(1)", 15, 32)] + [InlineData("substitute.Barr", "Dim x = callInfo(1)", 15, 40)] + [InlineData("substitute.Barr", "callInfo(1) = 1", 15, 32)] + [InlineData("substitute.Barr", "Dim x = callInfo.Args()(1)", 15, 40)] + [InlineData("substitute.Barr", "callInfo.Args()(1) = 1", 15, 32)] + [InlineData("substitute.Barr", "callInfo.ArgTypes()(1) = GetType(Integer)", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "Dim x = callInfo(1)", 15, 40)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo(1) = 1", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)", 15, 40)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.Args()(1) = 1", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.ArgTypes()(1) = GetType(Integer)", 15, 32)] + public override async Task ReportsDiagnostic_WhenAccessingArgumentOutOfBounds(string call, string argAccess, int expectedLine, int expectedColumn) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentOutOfRange, + Severity = DiagnosticSeverity.Warning, + Message = "There is no argument at position 1", + Locations = new[] + { + new DiagnosticResultLocation(expectedLine, expectedColumn) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(1)")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentWithinBounds(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "callInfo.ArgAt(Of Bar)(1)", 17, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo(1), Bar)", 17, 46)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo(1), Bar)", 17, 48)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo(1), Bar)", 17, 51)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo.Args()(1), Bar)", 17, 46)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo.Args()(1), Bar)", 17, 48)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo.Args()(1), Bar)", 17, 51)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "callInfo.ArgAt(Of Bar)(1)", 17, 32)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo(1), Bar)", 17, 46)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo(1), Bar)", 17, 48)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo(1), Bar)", 17, 51)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo.Args()(1), Bar)", 17, 46)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo.Args()(1), Bar)", 17, 48)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo.Args()(1), Bar)", 17, 51)] + public override async Task ReportsDiagnostic_WhenConvertingTypeToUnsupportedType(string call, string argAccess, int expectedLine, int expectedColumn) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoCouldNotConvertParameterAtPosition, + Severity = DiagnosticSeverity.Warning, + Message = "Couldn't convert parameter at position 1 to type MyNamespace.Bar.", + Locations = new[] + { + new DiagnosticResultLocation(expectedLine, expectedColumn) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.ArgAt(Of Bar)(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.Args()(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.ArgAt(Of Bar)(0)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.Args()(0), Bar)")] + public override async Task ReportsNoDiagnostic_WhenConvertingTypeToSupportedType(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes()(0), Object)")] + public override async Task ReportsNoDiagnostic_WhenCastingElementsFromArgTypes(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.ArgTypes()(0) = GetType(Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.Args()(0) = 1D")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.ArgTypes()(0) = GetType(Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.Args()(0) = 1D")] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToNotRefNorOutArgumentViaIndirectCall(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())")] + [InlineData("substitute.Barr")] + [InlineData("substitute(Arg.Any(Of Integer)())")] + public override async Task ReportsDiagnostic_WhenAccessingArgumentByTypeNotInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs({call}, Function(callInfo) + callInfo.Arg(Of Double)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoCouldNotFindArgumentToThisCall, + Severity = DiagnosticSeverity.Warning, + Message = "Can not find an argument of type Double to this call.", + Locations = new[] + { + new DiagnosticResultLocation(16, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())")] + [InlineData("substitute(Arg.Any(Of Integer)())")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeInInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs({call}, Function(callInfo) + callInfo.Arg(Of Integer)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())")] + public override async Task ReportsDiagnostic_WhenAccessingArgumentByTypeMultipleTimesInInvocation(string call) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Integer) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs({call}, Function(callInfo) + callInfo.Arg(Of Integer)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoMoreThanOneArgumentOfType, + Severity = DiagnosticSeverity.Warning, + Message = "There is more than one argument of type Integer to this call.", + Locations = new[] + { + new DiagnosticResultLocation(13, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeMultipleDifferentTypesInInvocation(string call) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs({call}, Function(callInfo) + callInfo.Arg(Of Integer)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + public override async Task ReportsDiagnostic_WhenAssigningValueToNotOutNorRefArgument(string call) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs({call}, Function(callInfo) + callInfo(1) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentIsNotOutOrRef, + Severity = DiagnosticSeverity.Warning, + Message = "Could not set argument 1 (Double) as it is not an out or ref argument.", + Locations = new[] + { + new DiagnosticResultLocation(13, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToRefArgument() + { + var source = @"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToOutArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Fact] + public override async Task ReportsDiagnostic_WhenAssigningValueToOutOfBoundsArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(substitute.Bar(value), Function(callInfo) + callInfo(1) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentOutOfRange, + Severity = DiagnosticSeverity.Warning, + Message = "There is no argument at position 1", + Locations = new[] + { + new DiagnosticResultLocation(14, 47) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsDiagnostic_WhenAssigningWrongTypeToArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Decimal) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Decimal = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentSetWithIncompatibleValue, + Severity = DiagnosticSeverity.Warning, + Message = "Could not set value of type Integer to argument 0 (Decimal) because the types are incompatible.", + Locations = new[] + { + new DiagnosticResultLocation(14, 47) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningProperTypeToArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Decimal) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Decimal = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1D + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ReturnsForAnyArgsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ReturnsForAnyArgsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs new file mode 100644 index 00000000..084a841b --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ReturnsForAnyArgsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs @@ -0,0 +1,610 @@ +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using NSubstitute.Analyzers.Shared; +using NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers; +using Xunit; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.CallInfoAnalyzerTests +{ + public class ReturnsForAnyArgsAsOrdinaryMethodWithGenericTypeSpecifiedTests : CallInfoDiagnosticVerifier + { + [Theory] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)", 15, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "Dim x = callInfo(1)", 15, 40)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo(1) = 1", 15, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)", 15, 40)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.Args()(1) = 1", 15, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.ArgTypes()(1) = GetType(Integer)", 15, 32)] + [InlineData("substitute.Barr", "callInfo.ArgAt(Of Integer)(1)", 15, 32)] + [InlineData("substitute.Barr", "Dim x = callInfo(1)", 15, 40)] + [InlineData("substitute.Barr", "callInfo(1) = 1", 15, 32)] + [InlineData("substitute.Barr", "Dim x = callInfo.Args()(1)", 15, 40)] + [InlineData("substitute.Barr", "callInfo.Args()(1) = 1", 15, 32)] + [InlineData("substitute.Barr", "callInfo.ArgTypes()(1) = GetType(Integer)", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "Dim x = callInfo(1)", 15, 40)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo(1) = 1", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)", 15, 40)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.Args()(1) = 1", 15, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.ArgTypes()(1) = GetType(Integer)", 15, 32)] + public override async Task ReportsDiagnostic_WhenAccessingArgumentOutOfBounds(string call, string argAccess, int expectedLine, int expectedColumn) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentOutOfRange, + Severity = DiagnosticSeverity.Warning, + Message = "There is no argument at position 1", + Locations = new[] + { + new DiagnosticResultLocation(expectedLine, expectedColumn) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(1)")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentWithinBounds(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "callInfo.ArgAt(Of Bar)(1)", 17, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo(1), Bar)", 17, 46)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo(1), Bar)", 17, 48)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo(1), Bar)", 17, 51)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo.Args()(1), Bar)", 17, 46)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo.Args()(1), Bar)", 17, 48)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo.Args()(1), Bar)", 17, 51)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "callInfo.ArgAt(Of Bar)(1)", 17, 32)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo(1), Bar)", 17, 46)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo(1), Bar)", 17, 48)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo(1), Bar)", 17, 51)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo.Args()(1), Bar)", 17, 46)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo.Args()(1), Bar)", 17, 48)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo.Args()(1), Bar)", 17, 51)] + public override async Task ReportsDiagnostic_WhenConvertingTypeToUnsupportedType(string call, string argAccess, int expectedLine, int expectedColumn) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoCouldNotConvertParameterAtPosition, + Severity = DiagnosticSeverity.Warning, + Message = "Couldn't convert parameter at position 1 to type MyNamespace.Bar.", + Locations = new[] + { + new DiagnosticResultLocation(expectedLine, expectedColumn) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.ArgAt(Of Bar)(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.Args()(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.ArgAt(Of Bar)(0)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.Args()(0), Bar)")] + public override async Task ReportsNoDiagnostic_WhenConvertingTypeToSupportedType(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes()(0), Object)")] + public override async Task ReportsNoDiagnostic_WhenCastingElementsFromArgTypes(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.ArgTypes()(0) = GetType(Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.Args()(0) = 1D")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.ArgTypes()(0) = GetType(Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.Args()(0) = 1D")] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToNotRefNorOutArgumentViaIndirectCall(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)({call}, Function(callInfo) + {argAccess} + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())")] + [InlineData("substitute.Barr")] + [InlineData("substitute(Arg.Any(Of Integer)())")] + public override async Task ReportsDiagnostic_WhenAccessingArgumentByTypeNotInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)({call}, Function(callInfo) + callInfo.Arg(Of Double)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoCouldNotFindArgumentToThisCall, + Severity = DiagnosticSeverity.Warning, + Message = "Can not find an argument of type Double to this call.", + Locations = new[] + { + new DiagnosticResultLocation(16, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())")] + [InlineData("substitute(Arg.Any(Of Integer)())")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeInInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)({call}, Function(callInfo) + callInfo.Arg(Of Integer)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())")] + public override async Task ReportsDiagnostic_WhenAccessingArgumentByTypeMultipleTimesInInvocation(string call) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Integer) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)({call}, Function(callInfo) + callInfo.Arg(Of Integer)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoMoreThanOneArgumentOfType, + Severity = DiagnosticSeverity.Warning, + Message = "There is more than one argument of type Integer to this call.", + Locations = new[] + { + new DiagnosticResultLocation(13, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeMultipleDifferentTypesInInvocation(string call) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)({call}, Function(callInfo) + callInfo.Arg(Of Integer)() + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + public override async Task ReportsDiagnostic_WhenAssigningValueToNotOutNorRefArgument(string call) + { + var source = $@"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)({call}, Function(callInfo) + callInfo(1) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentIsNotOutOrRef, + Severity = DiagnosticSeverity.Warning, + Message = "Could not set argument 1 (Double) as it is not an out or ref argument.", + Locations = new[] + { + new DiagnosticResultLocation(13, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToRefArgument() + { + var source = @"Imports NSubstitute + +Namespace MyNamespace + Interface Foo + Function Bar(ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToOutArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Fact] + public override async Task ReportsDiagnostic_WhenAssigningValueToOutOfBoundsArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Bar(value), Function(callInfo) + callInfo(1) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentOutOfRange, + Severity = DiagnosticSeverity.Warning, + Message = "There is no argument at position 1", + Locations = new[] + { + new DiagnosticResultLocation(14, 47) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsDiagnostic_WhenAssigningWrongTypeToArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Decimal) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Decimal = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1 + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentSetWithIncompatibleValue, + Severity = DiagnosticSeverity.Warning, + Message = "Could not set value of type Integer to argument 0 (Decimal) because the types are incompatible.", + Locations = new[] + { + new DiagnosticResultLocation(14, 47) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningProperTypeToArgument() + { + var source = @"Imports NSubstitute +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Decimal) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Decimal = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1D + Return 1 + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ThrowsAsExtensionMethodsTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ThrowsAsExtensionMethodsTests.cs new file mode 100644 index 00000000..0277ca38 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ThrowsAsExtensionMethodsTests.cs @@ -0,0 +1,634 @@ +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using NSubstitute.Analyzers.Shared; +using NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers; +using Xunit; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.CallInfoAnalyzerTests +{ + public class ThrowsAsExtensionMethodsTests : CallInfoDiagnosticVerifier + { + [Theory] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)", 16, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "Dim x = callInfo(1)", 16, 40)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo(1) = 1", 16, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)", 16, 40)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.Args()(1) = 1", 16, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.ArgTypes()(1) = GetType(Integer)", 16, 32)] + [InlineData("substitute.Barr", "callInfo.ArgAt(Of Integer)(1)", 16, 32)] + [InlineData("substitute.Barr", "Dim x = callInfo(1)", 16, 40)] + [InlineData("substitute.Barr", "callInfo(1) = 1", 16, 32)] + [InlineData("substitute.Barr", "Dim x = callInfo.Args()(1)", 16, 40)] + [InlineData("substitute.Barr", "callInfo.Args()(1) = 1", 16, 32)] + [InlineData("substitute.Barr", "callInfo.ArgTypes()(1) = GetType(Integer)", 16, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)", 16, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "Dim x = callInfo(1)", 16, 40)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo(1) = 1", 16, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)", 16, 40)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.Args()(1) = 1", 16, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.ArgTypes()(1) = GetType(Integer)", 16, 32)] + public override async Task ReportsDiagnostic_WhenAccessingArgumentOutOfBounds(string call, string argAccess, int expectedLine, int expectedColumn) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.Throws(Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentOutOfRange, + Severity = DiagnosticSeverity.Warning, + Message = "There is no argument at position 1", + Locations = new[] + { + new DiagnosticResultLocation(expectedLine, expectedColumn) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(1)")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentWithinBounds(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.Throws(Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "callInfo.ArgAt(Of Bar)(1)", 18, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo(1), Bar)", 18, 46)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo(1), Bar)", 18, 48)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo(1), Bar)", 18, 51)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo.Args()(1), Bar)", 18, 46)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo.Args()(1), Bar)", 18, 48)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo.Args()(1), Bar)", 18, 51)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "callInfo.ArgAt(Of Bar)(1)", 18, 32)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo(1), Bar)", 18, 46)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo(1), Bar)", 18, 48)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo(1), Bar)", 18, 51)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo.Args()(1), Bar)", 18, 46)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo.Args()(1), Bar)", 18, 48)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo.Args()(1), Bar)", 18, 51)] + public override async Task ReportsDiagnostic_WhenConvertingTypeToUnsupportedType(string call, string argAccess, int expectedLine, int expectedColumn) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.Throws(Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoCouldNotConvertParameterAtPosition, + Severity = DiagnosticSeverity.Warning, + Message = "Couldn't convert parameter at position 1 to type MyNamespace.Bar.", + Locations = new[] + { + new DiagnosticResultLocation(expectedLine, expectedColumn) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.ArgAt(Of Bar)(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.Args()(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.ArgAt(Of Bar)(0)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.Args()(0), Bar)")] + public override async Task ReportsNoDiagnostic_WhenConvertingTypeToSupportedType(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.Throws(Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes()(0), Object)")] + public override async Task ReportsNoDiagnostic_WhenCastingElementsFromArgTypes(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.Throws(Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.ArgTypes()(0) = GetType(Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.Args()(0) = 1D")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.ArgTypes()(0) = GetType(Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.Args()(0) = 1D")] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToNotRefNorOutArgumentViaIndirectCall(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.Throws(Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())")] + [InlineData("substitute.Barr")] + [InlineData("substitute(Arg.Any(Of Integer)())")] + public override async Task ReportsDiagnostic_WhenAccessingArgumentByTypeNotInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.Throws(Function(callInfo) + callInfo.Arg(Of Double)() + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoCouldNotFindArgumentToThisCall, + Severity = DiagnosticSeverity.Warning, + Message = "Can not find an argument of type Double to this call.", + Locations = new[] + { + new DiagnosticResultLocation(17, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())")] + [InlineData("substitute(Arg.Any(Of Integer)())")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeInInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.Throws(Function(callInfo) + callInfo.Arg(Of Integer)() + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())")] + public override async Task ReportsDiagnostic_WhenAccessingArgumentByTypeMultipleTimesInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Integer) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.Throws(Function(callInfo) + callInfo.Arg(Of Integer)() + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoMoreThanOneArgumentOfType, + Severity = DiagnosticSeverity.Warning, + Message = "There is more than one argument of type Integer to this call.", + Locations = new[] + { + new DiagnosticResultLocation(15, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeMultipleDifferentTypesInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.Throws(Function(callInfo) + callInfo.Arg(Of Integer)() + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + public override async Task ReportsDiagnostic_WhenAssigningValueToNotOutNorRefArgument(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.Throws(Function(callInfo) + callInfo(1) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentIsNotOutOrRef, + Severity = DiagnosticSeverity.Warning, + Message = "Could not set argument 1 (Double) as it is not an out or ref argument.", + Locations = new[] + { + new DiagnosticResultLocation(15, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToRefArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar(value).Throws(Function(callInfo) + callInfo(0) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToOutArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar(value).Throws(Function(callInfo) + callInfo(0) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Fact] + public override async Task ReportsDiagnostic_WhenAssigningValueToOutOfBoundsArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar(value).Throws(Function(callInfo) + callInfo(1) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentOutOfRange, + Severity = DiagnosticSeverity.Warning, + Message = "There is no argument at position 1", + Locations = new[] + { + new DiagnosticResultLocation(16, 47) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsDiagnostic_WhenAssigningWrongTypeToArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Decimal) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Decimal = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar(value).Throws(Function(callInfo) + callInfo(0) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentSetWithIncompatibleValue, + Severity = DiagnosticSeverity.Warning, + Message = "Could not set value of type Integer to argument 0 (Decimal) because the types are incompatible.", + Locations = new[] + { + new DiagnosticResultLocation(16, 47) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningProperTypeToArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Decimal) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Decimal = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar(value).Throws(Function(callInfo) + callInfo(0) = 1D + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ThrowsAsOrdinaryMethodsTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ThrowsAsOrdinaryMethodsTests.cs new file mode 100644 index 00000000..7c7f4a08 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ThrowsAsOrdinaryMethodsTests.cs @@ -0,0 +1,634 @@ +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using NSubstitute.Analyzers.Shared; +using NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers; +using Xunit; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.CallInfoAnalyzerTests +{ + public class ThrowsAsOrdinaryMethodsTests : CallInfoDiagnosticVerifier + { + [Theory] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)", 16, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "Dim x = callInfo(1)", 16, 40)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo(1) = 1", 16, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)", 16, 40)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.Args()(1) = 1", 16, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.ArgTypes()(1) = GetType(Integer)", 16, 32)] + [InlineData("substitute.Barr", "callInfo.ArgAt(Of Integer)(1)", 16, 32)] + [InlineData("substitute.Barr", "Dim x = callInfo(1)", 16, 40)] + [InlineData("substitute.Barr", "callInfo(1) = 1", 16, 32)] + [InlineData("substitute.Barr", "Dim x = callInfo.Args()(1)", 16, 40)] + [InlineData("substitute.Barr", "callInfo.Args()(1) = 1", 16, 32)] + [InlineData("substitute.Barr", "callInfo.ArgTypes()(1) = GetType(Integer)", 16, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)", 16, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "Dim x = callInfo(1)", 16, 40)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo(1) = 1", 16, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)", 16, 40)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.Args()(1) = 1", 16, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.ArgTypes()(1) = GetType(Integer)", 16, 32)] + public override async Task ReportsDiagnostic_WhenAccessingArgumentOutOfBounds(string call, string argAccess, int expectedLine, int expectedColumn) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.Throws({call}, Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentOutOfRange, + Severity = DiagnosticSeverity.Warning, + Message = "There is no argument at position 1", + Locations = new[] + { + new DiagnosticResultLocation(expectedLine, expectedColumn) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(1)")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentWithinBounds(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.Throws({call}, Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "callInfo.ArgAt(Of Bar)(1)", 18, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo(1), Bar)", 18, 46)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo(1), Bar)", 18, 48)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo(1), Bar)", 18, 51)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo.Args()(1), Bar)", 18, 46)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo.Args()(1), Bar)", 18, 48)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo.Args()(1), Bar)", 18, 51)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "callInfo.ArgAt(Of Bar)(1)", 18, 32)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo(1), Bar)", 18, 46)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo(1), Bar)", 18, 48)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo(1), Bar)", 18, 51)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo.Args()(1), Bar)", 18, 46)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo.Args()(1), Bar)", 18, 48)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo.Args()(1), Bar)", 18, 51)] + public override async Task ReportsDiagnostic_WhenConvertingTypeToUnsupportedType(string call, string argAccess, int expectedLine, int expectedColumn) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.Throws({call}, Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoCouldNotConvertParameterAtPosition, + Severity = DiagnosticSeverity.Warning, + Message = "Couldn't convert parameter at position 1 to type MyNamespace.Bar.", + Locations = new[] + { + new DiagnosticResultLocation(expectedLine, expectedColumn) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.ArgAt(Of Bar)(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.Args()(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.ArgAt(Of Bar)(0)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.Args()(0), Bar)")] + public override async Task ReportsNoDiagnostic_WhenConvertingTypeToSupportedType(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.Throws({call}, Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes()(0), Object)")] + public override async Task ReportsNoDiagnostic_WhenCastingElementsFromArgTypes(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.Throws({call}, Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.ArgTypes()(0) = GetType(Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.Args()(0) = 1D")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.ArgTypes()(0) = GetType(Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.Args()(0) = 1D")] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToNotRefNorOutArgumentViaIndirectCall(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.Throws({call}, Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())")] + [InlineData("substitute.Barr")] + [InlineData("substitute(Arg.Any(Of Integer)())")] + public override async Task ReportsDiagnostic_WhenAccessingArgumentByTypeNotInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.Throws({call}, Function(callInfo) + callInfo.Arg(Of Double)() + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoCouldNotFindArgumentToThisCall, + Severity = DiagnosticSeverity.Warning, + Message = "Can not find an argument of type Double to this call.", + Locations = new[] + { + new DiagnosticResultLocation(17, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())")] + [InlineData("substitute(Arg.Any(Of Integer)())")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeInInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.Throws({call}, Function(callInfo) + callInfo.Arg(Of Integer)() + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())")] + public override async Task ReportsDiagnostic_WhenAccessingArgumentByTypeMultipleTimesInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Integer) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.Throws({call}, Function(callInfo) + callInfo.Arg(Of Integer)() + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoMoreThanOneArgumentOfType, + Severity = DiagnosticSeverity.Warning, + Message = "There is more than one argument of type Integer to this call.", + Locations = new[] + { + new DiagnosticResultLocation(15, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeMultipleDifferentTypesInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.Throws({call}, Function(callInfo) + callInfo.Arg(Of Integer)() + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + public override async Task ReportsDiagnostic_WhenAssigningValueToNotOutNorRefArgument(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.Throws({call}, Function(callInfo) + callInfo(1) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentIsNotOutOrRef, + Severity = DiagnosticSeverity.Warning, + Message = "Could not set argument 1 (Double) as it is not an out or ref argument.", + Locations = new[] + { + new DiagnosticResultLocation(15, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToRefArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.Throws(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToOutArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.Throws(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Fact] + public override async Task ReportsDiagnostic_WhenAssigningValueToOutOfBoundsArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.Throws(substitute.Bar(value), Function(callInfo) + callInfo(1) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentOutOfRange, + Severity = DiagnosticSeverity.Warning, + Message = "There is no argument at position 1", + Locations = new[] + { + new DiagnosticResultLocation(16, 47) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsDiagnostic_WhenAssigningWrongTypeToArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Decimal) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Decimal = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.Throws(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentSetWithIncompatibleValue, + Severity = DiagnosticSeverity.Warning, + Message = "Could not set value of type Integer to argument 0 (Decimal) because the types are incompatible.", + Locations = new[] + { + new DiagnosticResultLocation(16, 47) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningProperTypeToArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Decimal) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Decimal = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.Throws(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1D + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ThrowsForAnyArgsAsExtensionMethodsTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ThrowsForAnyArgsAsExtensionMethodsTests.cs new file mode 100644 index 00000000..9fb2df1c --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ThrowsForAnyArgsAsExtensionMethodsTests.cs @@ -0,0 +1,634 @@ +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using NSubstitute.Analyzers.Shared; +using NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers; +using Xunit; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.CallInfoAnalyzerTests +{ + public class ThrowsForAnyArgsAsExtensionMethodsTests : CallInfoDiagnosticVerifier + { + [Theory] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)", 16, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "Dim x = callInfo(1)", 16, 40)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo(1) = 1", 16, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)", 16, 40)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.Args()(1) = 1", 16, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.ArgTypes()(1) = GetType(Integer)", 16, 32)] + [InlineData("substitute.Barr", "callInfo.ArgAt(Of Integer)(1)", 16, 32)] + [InlineData("substitute.Barr", "Dim x = callInfo(1)", 16, 40)] + [InlineData("substitute.Barr", "callInfo(1) = 1", 16, 32)] + [InlineData("substitute.Barr", "Dim x = callInfo.Args()(1)", 16, 40)] + [InlineData("substitute.Barr", "callInfo.Args()(1) = 1", 16, 32)] + [InlineData("substitute.Barr", "callInfo.ArgTypes()(1) = GetType(Integer)", 16, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)", 16, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "Dim x = callInfo(1)", 16, 40)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo(1) = 1", 16, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)", 16, 40)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.Args()(1) = 1", 16, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.ArgTypes()(1) = GetType(Integer)", 16, 32)] + public override async Task ReportsDiagnostic_WhenAccessingArgumentOutOfBounds(string call, string argAccess, int expectedLine, int expectedColumn) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ThrowsForAnyArgs(Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentOutOfRange, + Severity = DiagnosticSeverity.Warning, + Message = "There is no argument at position 1", + Locations = new[] + { + new DiagnosticResultLocation(expectedLine, expectedColumn) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(1)")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentWithinBounds(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ThrowsForAnyArgs(Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "callInfo.ArgAt(Of Bar)(1)", 18, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo(1), Bar)", 18, 46)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo(1), Bar)", 18, 48)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo(1), Bar)", 18, 51)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo.Args()(1), Bar)", 18, 46)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo.Args()(1), Bar)", 18, 48)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo.Args()(1), Bar)", 18, 51)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "callInfo.ArgAt(Of Bar)(1)", 18, 32)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo(1), Bar)", 18, 46)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo(1), Bar)", 18, 48)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo(1), Bar)", 18, 51)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo.Args()(1), Bar)", 18, 46)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo.Args()(1), Bar)", 18, 48)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo.Args()(1), Bar)", 18, 51)] + public override async Task ReportsDiagnostic_WhenConvertingTypeToUnsupportedType(string call, string argAccess, int expectedLine, int expectedColumn) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ThrowsForAnyArgs(Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoCouldNotConvertParameterAtPosition, + Severity = DiagnosticSeverity.Warning, + Message = "Couldn't convert parameter at position 1 to type MyNamespace.Bar.", + Locations = new[] + { + new DiagnosticResultLocation(expectedLine, expectedColumn) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.ArgAt(Of Bar)(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.Args()(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.ArgAt(Of Bar)(0)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.Args()(0), Bar)")] + public override async Task ReportsNoDiagnostic_WhenConvertingTypeToSupportedType(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ThrowsForAnyArgs(Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes()(0), Object)")] + public override async Task ReportsNoDiagnostic_WhenCastingElementsFromArgTypes(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ThrowsForAnyArgs(Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.ArgTypes()(0) = GetType(Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.Args()(0) = 1D")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.ArgTypes()(0) = GetType(Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.Args()(0) = 1D")] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToNotRefNorOutArgumentViaIndirectCall(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ThrowsForAnyArgs(Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())")] + [InlineData("substitute.Barr")] + [InlineData("substitute(Arg.Any(Of Integer)())")] + public override async Task ReportsDiagnostic_WhenAccessingArgumentByTypeNotInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ThrowsForAnyArgs(Function(callInfo) + callInfo.Arg(Of Double)() + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoCouldNotFindArgumentToThisCall, + Severity = DiagnosticSeverity.Warning, + Message = "Can not find an argument of type Double to this call.", + Locations = new[] + { + new DiagnosticResultLocation(17, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())")] + [InlineData("substitute(Arg.Any(Of Integer)())")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeInInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ThrowsForAnyArgs(Function(callInfo) + callInfo.Arg(Of Integer)() + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())")] + public override async Task ReportsDiagnostic_WhenAccessingArgumentByTypeMultipleTimesInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Integer) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ThrowsForAnyArgs(Function(callInfo) + callInfo.Arg(Of Integer)() + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoMoreThanOneArgumentOfType, + Severity = DiagnosticSeverity.Warning, + Message = "There is more than one argument of type Integer to this call.", + Locations = new[] + { + new DiagnosticResultLocation(15, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeMultipleDifferentTypesInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ThrowsForAnyArgs(Function(callInfo) + callInfo.Arg(Of Integer)() + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + public override async Task ReportsDiagnostic_WhenAssigningValueToNotOutNorRefArgument(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {call}.ThrowsForAnyArgs(Function(callInfo) + callInfo(1) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentIsNotOutOrRef, + Severity = DiagnosticSeverity.Warning, + Message = "Could not set argument 1 (Double) as it is not an out or ref argument.", + Locations = new[] + { + new DiagnosticResultLocation(15, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToRefArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar(value).ThrowsForAnyArgs(Function(callInfo) + callInfo(0) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToOutArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar(value).ThrowsForAnyArgs(Function(callInfo) + callInfo(0) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Fact] + public override async Task ReportsDiagnostic_WhenAssigningValueToOutOfBoundsArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar(value).ThrowsForAnyArgs(Function(callInfo) + callInfo(1) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentOutOfRange, + Severity = DiagnosticSeverity.Warning, + Message = "There is no argument at position 1", + Locations = new[] + { + new DiagnosticResultLocation(16, 47) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsDiagnostic_WhenAssigningWrongTypeToArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Decimal) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Decimal = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar(value).ThrowsForAnyArgs(Function(callInfo) + callInfo(0) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentSetWithIncompatibleValue, + Severity = DiagnosticSeverity.Warning, + Message = "Could not set value of type Integer to argument 0 (Decimal) because the types are incompatible.", + Locations = new[] + { + new DiagnosticResultLocation(16, 47) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningProperTypeToArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Decimal) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Decimal = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar(value).ThrowsForAnyArgs(Function(callInfo) + callInfo(0) = 1D + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ThrowsForAnyArgsAsOrdinaryMethodsTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ThrowsForAnyArgsAsOrdinaryMethodsTests.cs new file mode 100644 index 00000000..dad8b6e2 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/CallInfoAnalyzerTests/ThrowsForAnyArgsAsOrdinaryMethodsTests.cs @@ -0,0 +1,634 @@ +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using NSubstitute.Analyzers.Shared; +using NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers; +using Xunit; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.CallInfoAnalyzerTests +{ + public class ThrowsForAnyArgsAsOrdinaryMethodsTests : CallInfoDiagnosticVerifier + { + [Theory] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)", 16, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "Dim x = callInfo(1)", 16, 40)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo(1) = 1", 16, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)", 16, 40)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.Args()(1) = 1", 16, 32)] + [InlineData("substitute(Arg.Any(Of Integer)())", "callInfo.ArgTypes()(1) = GetType(Integer)", 16, 32)] + [InlineData("substitute.Barr", "callInfo.ArgAt(Of Integer)(1)", 16, 32)] + [InlineData("substitute.Barr", "Dim x = callInfo(1)", 16, 40)] + [InlineData("substitute.Barr", "callInfo(1) = 1", 16, 32)] + [InlineData("substitute.Barr", "Dim x = callInfo.Args()(1)", 16, 40)] + [InlineData("substitute.Barr", "callInfo.Args()(1) = 1", 16, 32)] + [InlineData("substitute.Barr", "callInfo.ArgTypes()(1) = GetType(Integer)", 16, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)", 16, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "Dim x = callInfo(1)", 16, 40)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo(1) = 1", 16, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)", 16, 40)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.Args()(1) = 1", 16, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())", "callInfo.ArgTypes()(1) = GetType(Integer)", 16, 32)] + public override async Task ReportsDiagnostic_WhenAccessingArgumentOutOfBounds(string call, string argAccess, int expectedLine, int expectedColumn) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.ThrowsForAnyArgs({call}, Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentOutOfRange, + Severity = DiagnosticSeverity.Warning, + Message = "There is no argument at position 1", + Locations = new[] + { + new DiagnosticResultLocation(expectedLine, expectedColumn) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(0)")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "callInfo.ArgAt(Of Integer)(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.Args()(1)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())", "Dim x = callInfo.ArgTypes()(1)")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentWithinBounds(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.ThrowsForAnyArgs({call}, Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "callInfo.ArgAt(Of Bar)(1)", 18, 32)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo(1), Bar)", 18, 46)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo(1), Bar)", 18, 48)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo(1), Bar)", 18, 51)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo.Args()(1), Bar)", 18, 46)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo.Args()(1), Bar)", 18, 48)] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo.Args()(1), Bar)", 18, 51)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "callInfo.ArgAt(Of Bar)(1)", 18, 32)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo(1), Bar)", 18, 46)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo(1), Bar)", 18, 48)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo(1), Bar)", 18, 51)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = CType(callInfo.Args()(1), Bar)", 18, 46)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = TryCast(callInfo.Args()(1), Bar)", 18, 48)] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())", "Dim x = DirectCast(callInfo.Args()(1), Bar)", 18, 51)] + public override async Task ReportsDiagnostic_WhenConvertingTypeToUnsupportedType(string call, string argAccess, int expectedLine, int expectedColumn) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.ThrowsForAnyArgs({call}, Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoCouldNotConvertParameterAtPosition, + Severity = DiagnosticSeverity.Warning, + Message = "Couldn't convert parameter at position 1 to type MyNamespace.Bar.", + Locations = new[] + { + new DiagnosticResultLocation(expectedLine, expectedColumn) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.ArgAt(Of Bar)(0)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.Args()(0), Bar)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.ArgAt(Of Bar)(0)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.Args()(0), Bar)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.Args()(0), Bar)")] + public override async Task ReportsNoDiagnostic_WhenConvertingTypeToSupportedType(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.ThrowsForAnyArgs({call}, Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes(), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes(), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = TryCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = DirectCast(callInfo.ArgTypes()(0), Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "Dim x = CType(callInfo.ArgTypes()(0), Object)")] + public override async Task ReportsNoDiagnostic_WhenCastingElementsFromArgTypes(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.ThrowsForAnyArgs({call}, Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.ArgTypes()(0) = GetType(Object)")] + [InlineData("substitute.Bar(Arg.Any(Of Bar)())", "callInfo.Args()(0) = 1D")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.ArgTypes()(0) = GetType(Object)")] + [InlineData("substitute(Arg.Any(Of Bar)())", "callInfo.Args()(0) = 1D")] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToNotRefNorOutArgumentViaIndirectCall(string call, string argAccess) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Bar) As Integer + Default ReadOnly Property Item(ByVal x As Bar) As Integer + End Interface + + Public Class Bar + End Class + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.ThrowsForAnyArgs({call}, Function(callInfo) + {argAccess} + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())")] + [InlineData("substitute.Barr")] + [InlineData("substitute(Arg.Any(Of Integer)())")] + public override async Task ReportsDiagnostic_WhenAccessingArgumentByTypeNotInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + ReadOnly Property Barr As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.ThrowsForAnyArgs({call}, Function(callInfo) + callInfo.Arg(Of Double)() + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoCouldNotFindArgumentToThisCall, + Severity = DiagnosticSeverity.Warning, + Message = "Can not find an argument of type Double to this call.", + Locations = new[] + { + new DiagnosticResultLocation(17, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)())")] + [InlineData("substitute(Arg.Any(Of Integer)())")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeInInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Integer + Default ReadOnly Property Item(ByVal x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.ThrowsForAnyArgs({call}, Function(callInfo) + callInfo.Arg(Of Integer)() + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Integer)())")] + public override async Task ReportsDiagnostic_WhenAccessingArgumentByTypeMultipleTimesInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Integer) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.ThrowsForAnyArgs({call}, Function(callInfo) + callInfo.Arg(Of Integer)() + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoMoreThanOneArgumentOfType, + Severity = DiagnosticSeverity.Warning, + Message = "There is more than one argument of type Integer to this call.", + Locations = new[] + { + new DiagnosticResultLocation(15, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + public override async Task ReportsNoDiagnostic_WhenAccessingArgumentByTypeMultipleDifferentTypesInInvocation(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.ThrowsForAnyArgs({call}, Function(callInfo) + callInfo.Arg(Of Integer)() + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + + [Theory] + [InlineData("substitute.Bar(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + [InlineData("substitute(Arg.Any(Of Integer)(), Arg.Any(Of Double)())")] + public override async Task ReportsDiagnostic_WhenAssigningValueToNotOutNorRefArgument(string call) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer, ByVal y As Double) As Integer + Default ReadOnly Property Item(ByVal x As Integer, ByVal y As Double) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.ThrowsForAnyArgs({call}, Function(callInfo) + callInfo(1) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentIsNotOutOrRef, + Severity = DiagnosticSeverity.Warning, + Message = "Could not set argument 1 (Double) as it is not an out or ref argument.", + Locations = new[] + { + new DiagnosticResultLocation(15, 32) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToRefArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.ThrowsForAnyArgs(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningValueToOutArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.ThrowsForAnyArgs(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source); + } + + [Fact] + public override async Task ReportsDiagnostic_WhenAssigningValueToOutOfBoundsArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Integer) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Integer = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.ThrowsForAnyArgs(substitute.Bar(value), Function(callInfo) + callInfo(1) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentOutOfRange, + Severity = DiagnosticSeverity.Warning, + Message = "There is no argument at position 1", + Locations = new[] + { + new DiagnosticResultLocation(16, 47) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsDiagnostic_WhenAssigningWrongTypeToArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Decimal) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Decimal = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.ThrowsForAnyArgs(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1 + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + var expectedDiagnostic = new DiagnosticResult + { + Id = DiagnosticIdentifiers.CallInfoArgumentSetWithIncompatibleValue, + Severity = DiagnosticSeverity.Warning, + Message = "Could not set value of type Integer to argument 0 (Decimal) because the types are incompatible.", + Locations = new[] + { + new DiagnosticResultLocation(16, 47) + } + }; + + await VerifyDiagnostic(source, expectedDiagnostic); + } + + [Fact] + public override async Task ReportsNoDiagnostic_WhenAssigningProperTypeToArgument() + { + var source = @"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions +Imports System.Runtime.InteropServices + +Namespace MyNamespace + Interface Foo + Function Bar( ByRef x As Decimal) As Integer + End Interface + + Public Class FooTests + Public Sub Test() + Dim value As Decimal = 0 + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + ExceptionExtensions.ThrowsForAnyArgs(substitute.Bar(value), Function(callInfo) + callInfo(0) = 1D + Return New Exception() + End Function) + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source); + } + } +} \ No newline at end of file