diff --git a/src/NSubstitute.Analyzers.CSharp/DiagnosticAnalyzers/ConflictingArgumentAssignmentsAnalyzer.cs b/src/NSubstitute.Analyzers.CSharp/DiagnosticAnalyzers/ConflictingArgumentAssignmentsAnalyzer.cs index 11c1dde4..e2d78a83 100644 --- a/src/NSubstitute.Analyzers.CSharp/DiagnosticAnalyzers/ConflictingArgumentAssignmentsAnalyzer.cs +++ b/src/NSubstitute.Analyzers.CSharp/DiagnosticAnalyzers/ConflictingArgumentAssignmentsAnalyzer.cs @@ -27,22 +27,7 @@ protected override IEnumerable GetArgumentExpressions(Invocati protected override SyntaxNode GetSubstituteCall(SyntaxNodeAnalysisContext syntaxNodeContext, IMethodSymbol methodSymbol, InvocationExpressionSyntax invocationExpressionSyntax) { - if (methodSymbol.IsExtensionMethod) - { - switch (methodSymbol.MethodKind) - { - case MethodKind.ReducedExtension: - return invocationExpressionSyntax.Expression.DescendantNodes().First(); - case MethodKind.Ordinary: - return invocationExpressionSyntax.ArgumentList.Arguments.First().Expression; - default: - return null; - } - } - - var parentInvocation = invocationExpressionSyntax.GetParentInvocationExpression(); - - return parentInvocation; + return invocationExpressionSyntax.GetParentInvocationExpression(); } protected override AbstractCallInfoFinder GetCallInfoFinder() diff --git a/src/NSubstitute.Analyzers.VisualBasic/DiagnosticAnalyzers/ConflictingArgumentAssignmentsAnalyzer.cs b/src/NSubstitute.Analyzers.VisualBasic/DiagnosticAnalyzers/ConflictingArgumentAssignmentsAnalyzer.cs new file mode 100644 index 00000000..363564cc --- /dev/null +++ b/src/NSubstitute.Analyzers.VisualBasic/DiagnosticAnalyzers/ConflictingArgumentAssignmentsAnalyzer.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.VisualBasic; +using Microsoft.CodeAnalysis.VisualBasic.Syntax; +using NSubstitute.Analyzers.Shared.DiagnosticAnalyzers; +using NSubstitute.Analyzers.VisualBasic.Extensions; + +namespace NSubstitute.Analyzers.VisualBasic.DiagnosticAnalyzers +{ + [DiagnosticAnalyzer(LanguageNames.VisualBasic)] + internal class ConflictingArgumentAssignmentsAnalyzer : AbstractConflictingArgumentAssignmentsAnalyzer + { + public ConflictingArgumentAssignmentsAnalyzer() + : base(new DiagnosticDescriptorsProvider()) + { + } + + protected override SyntaxKind InvocationExpressionKind { get; } = SyntaxKind.InvocationExpression; + + protected override IEnumerable GetArgumentExpressions(InvocationExpressionSyntax invocationExpressionSyntax) + { + return invocationExpressionSyntax.ArgumentList.Arguments.Select(arg => arg.GetExpression()); + } + + protected override SyntaxNode GetSubstituteCall(SyntaxNodeAnalysisContext syntaxNodeContext, IMethodSymbol methodSymbol, InvocationExpressionSyntax invocationExpressionSyntax) + { + return invocationExpressionSyntax.GetParentInvocationExpression(); + } + + protected override AbstractCallInfoFinder GetCallInfoFinder() + { + return new CallInfoCallFinder(); + } + + protected override int? GetIndexerPosition(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext, InvocationExpressionSyntax indexerExpressionSyntax) + { + var position = syntaxNodeAnalysisContext.SemanticModel.GetConstantValue(indexerExpressionSyntax.ArgumentList.Arguments.First().GetExpression()); + return (int?)(position.HasValue ? position.Value : null); + } + + protected override ISymbol GetIndexerSymbol(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext, InvocationExpressionSyntax indexerExpressionSyntax) + { + return ModelExtensions.GetSymbolInfo(syntaxNodeAnalysisContext.SemanticModel, indexerExpressionSyntax).Symbol ?? + ModelExtensions.GetSymbolInfo(syntaxNodeAnalysisContext.SemanticModel, indexerExpressionSyntax.Expression).Symbol; + } + + protected override SyntaxNode GetAssignmentExpression(InvocationExpressionSyntax indexerExpressionSyntax) + { + if (indexerExpressionSyntax.Parent is AssignmentStatementSyntax assignmentExpressionSyntax) + { + return assignmentExpressionSyntax.Right; + } + + return null; + } + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ConflictingArgumentAssignmentsAnalyzerTests/AndDoesAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ConflictingArgumentAssignmentsAnalyzerTests/AndDoesAsOrdinaryMethodTests.cs new file mode 100644 index 00000000..a65a2891 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ConflictingArgumentAssignmentsAnalyzerTests/AndDoesAsOrdinaryMethodTests.cs @@ -0,0 +1,179 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.ConflictingArgumentAssignmentsAnalyzerTests +{ + [CombinatoryData("AndDoes")] + public class AndDoesAsOrdinaryMethodTests : ConflictingArgumentAssignmentsDiagnosticVerifier + { + public override async Task ReportsDiagnostic_When_AndDoesMethod_SetsSameArgument_AsPreviousSetupMethod(string method, string call, string previousCallArgAccess, string andDoesArgAccess) + { + var source = $@"using System; +using NSubstitute; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {call}.Returns(1).{method}(callInfo => + {{ + {previousCallArgAccess} + }}).AndDoes(callInfo => + {{ + {andDoesArgAccess} + }}); + }} + }} +}}"; + + await VerifyDiagnostic(source, Descriptor); + } + + public override async Task ReportsNoDiagnostics_When_AndDoesMethod_SetsDifferentArgument_AsPreviousSetupMethod(string method, string call, string andDoesArgAccess) + { + var source = $@"using System; +using NSubstitute; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {call}.Returns(1).{method}(callInfo => + {{ + callInfo[0] = 1; + }}).AndDoes(callInfo => + {{ + {andDoesArgAccess} + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + public override async Task ReportsNoDiagnostics_When_AndDoesMethod_AccessSameArguments_AsPreviousSetupMethod(string method, string call, string argAccess) + { + var source = $@"using System; +using NSubstitute; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {call}.Returns(1).{method}(callInfo => + {{ + {argAccess} + }}).AndDoes(callInfo => + {{ + {argAccess} + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + public override async Task ReportsNoDiagnostics_When_AndDoesMethod_SetSameArguments_AsPreviousSetupMethod_SetsIndirectly(string method) + { + var source = $@"using System; +using NSubstitute; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + substitute.Bar(Arg.Any()).Returns(1).{method}(callInfo => + {{ + callInfo.Args()[0] = 1; + callInfo.ArgTypes()[0] = typeof(int); + ((byte[])callInfo[0])[0] = 1; + }}).AndDoes(callInfo => + {{ + callInfo[0] = 1; + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + public override async Task ReportsNoDiagnostic_When_AndDoesMethod_SetArgument_AndPreviousMethod_IsNotUsedWithCallInfo(string method, string call, string andDoesArgAccess) + { + var source = $@"using System; +using NSubstitute; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {call}.Returns(1).{method}(_ => {{}}).AndDoes(callInfo => + {{ + {andDoesArgAccess} + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ConflictingArgumentAssignmentsAnalyzerTests/ConflictingArgumentAssignmentsDiagnosticVerifier.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ConflictingArgumentAssignmentsAnalyzerTests/ConflictingArgumentAssignmentsDiagnosticVerifier.cs new file mode 100644 index 00000000..f54469c3 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ConflictingArgumentAssignmentsAnalyzerTests/ConflictingArgumentAssignmentsDiagnosticVerifier.cs @@ -0,0 +1,50 @@ +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using NSubstitute.Analyzers.Shared; +using NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; +using NSubstitute.Analyzers.VisualBasic; +using NSubstitute.Analyzers.VisualBasic.DiagnosticAnalyzers; +using Xunit; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.ConflictingArgumentAssignmentsAnalyzerTests +{ + public abstract class ConflictingArgumentAssignmentsDiagnosticVerifier : VisualBasicDiagnosticVerifier, IConflictingArgumentAssignmentsDiagnosticVerifier + { + public DiagnosticDescriptor Descriptor { get; } = DiagnosticDescriptors.ConflictingAssignmentsToOutRefArgument; + + [CombinatoryTheory] + [InlineData("substitute.Bar(Arg.Any())", "callInfo[1] = 1;", "[|callInfo[1]|] = 1;")] + [InlineData("substitute.Barr", "callInfo[1] = 1;", "[|callInfo[1]|] = 1;")] + [InlineData("substitute[Arg.Any()]", "callInfo[1] = 1;", "[|callInfo[1]|] = 1;")] + public abstract Task ReportsDiagnostic_When_AndDoesMethod_SetsSameArgument_AsPreviousSetupMethod(string method, string call, string previousCallArgAccess, string andDoesArgAccess); + + [CombinatoryTheory] + [InlineData("substitute.Bar(Arg.Any())", "callInfo[1] = 1;")] + [InlineData("substitute.Barr", "callInfo[1] = 1;")] + [InlineData("substitute[Arg.Any()]", "callInfo[1] = 1;")] + public abstract Task ReportsNoDiagnostics_When_AndDoesMethod_SetsDifferentArgument_AsPreviousSetupMethod(string method, string call, string andDoesArgAccess); + + [CombinatoryTheory] + [InlineData("substitute.Bar(Arg.Any())", "var x = callInfo[1];")] + [InlineData("substitute.Barr", "var x = callInfo[1];")] + [InlineData("substitute[Arg.Any()]", "var x = callInfo[1];")] + public abstract Task ReportsNoDiagnostics_When_AndDoesMethod_AccessSameArguments_AsPreviousSetupMethod(string method, string call, string argAccess); + + [CombinatoryTheory] + [InlineData] + public abstract Task ReportsNoDiagnostics_When_AndDoesMethod_SetSameArguments_AsPreviousSetupMethod_SetsIndirectly(string method); + + [CombinatoryTheory] + [InlineData("substitute.Bar(Arg.Any())", "callInfo[1] = 1;")] + [InlineData("substitute.Barr", "callInfo[1] = 1;")] + [InlineData("substitute[Arg.Any()]", "callInfo[1] = 1;")] + public abstract Task ReportsNoDiagnostic_When_AndDoesMethod_SetArgument_AndPreviousMethod_IsNotUsedWithCallInfo(string method, string call, string andDoesArgAccess); + + protected override DiagnosticAnalyzer GetDiagnosticAnalyzer() + { + return new ConflictingArgumentAssignmentsAnalyzer(); + } + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ConflictingArgumentAssignmentsAnalyzerTests/ReturnsAsExtensionMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ConflictingArgumentAssignmentsAnalyzerTests/ReturnsAsExtensionMethodTests.cs new file mode 100644 index 00000000..a719922f --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ConflictingArgumentAssignmentsAnalyzerTests/ReturnsAsExtensionMethodTests.cs @@ -0,0 +1,184 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.ConflictingArgumentAssignmentsAnalyzerTests +{ + [CombinatoryData("Returns", "Returns(Of Integer)", "ReturnsForAnyArgs", "ReturnsForAnyArgs(Of Integer)")] + public class ReturnsAsExtensionMethodTests : ConflictingArgumentAssignmentsDiagnosticVerifier + { + public override async Task ReportsDiagnostic_When_AndDoesMethod_SetsSameArgument_AsPreviousSetupMethod(string method, string call, string previousCallArgAccess, string andDoesArgAccess) + { + var source = $@"using System; +using NSubstitute; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {call}.{method}(callInfo => 1, + callInfo => + {{ + {previousCallArgAccess} + return 1; + }}).AndDoes(callInfo => + {{ + {andDoesArgAccess} + }}); + }} + }} +}}"; + + await VerifyDiagnostic(source, Descriptor); + } + + public override async Task ReportsNoDiagnostics_When_AndDoesMethod_SetsDifferentArgument_AsPreviousSetupMethod(string method, string call, string andDoesArgAccess) + { + var source = $@"using System; +using NSubstitute; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {call}.{method}(callInfo => + {{ + callInfo[0] = 1; + return 1; + }}).AndDoes(callInfo => + {{ + {andDoesArgAccess} + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + public override async Task ReportsNoDiagnostics_When_AndDoesMethod_AccessSameArguments_AsPreviousSetupMethod(string method, string call, string argAccess) + { + var source = $@"using System; +using NSubstitute; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {call}.{method}(callInfo => + {{ + {argAccess} + return 1; + }}).AndDoes(callInfo => + {{ + {argAccess} + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + public override async Task ReportsNoDiagnostics_When_AndDoesMethod_SetSameArguments_AsPreviousSetupMethod_SetsIndirectly(string method) + { + var source = $@"using System; +using NSubstitute; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + substitute.Bar(Arg.Any()).{method}(callInfo => + {{ + callInfo.Args()[0] = 1; + callInfo.ArgTypes()[0] = typeof(int); + ((byte[])callInfo[0])[0] = 1; + return 1; + }}).AndDoes(callInfo => + {{ + callInfo[0] = 1; + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + public override async Task ReportsNoDiagnostic_When_AndDoesMethod_SetArgument_AndPreviousMethod_IsNotUsedWithCallInfo(string method, string call, string andDoesArgAccess) + { + var source = $@"using System; +using NSubstitute; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {call}.{method}(1).AndDoes(callInfo => + {{ + {andDoesArgAccess} + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ConflictingArgumentAssignmentsAnalyzerTests/ReturnsAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ConflictingArgumentAssignmentsAnalyzerTests/ReturnsAsOrdinaryMethodTests.cs new file mode 100644 index 00000000..c6cf7ab1 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ConflictingArgumentAssignmentsAnalyzerTests/ReturnsAsOrdinaryMethodTests.cs @@ -0,0 +1,184 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.ConflictingArgumentAssignmentsAnalyzerTests +{ + [CombinatoryData("SubstituteExtensions.Returns", "SubstituteExtensions.Returns", "SubstituteExtensions.ReturnsForAnyArgs", "SubstituteExtensions.ReturnsForAnyArgs")] + public class ReturnsAsOrdinaryMethodTests : ConflictingArgumentAssignmentsDiagnosticVerifier + { + public override async Task ReportsDiagnostic_When_AndDoesMethod_SetsSameArgument_AsPreviousSetupMethod(string method, string call, string previousCallArgAccess, string andDoesArgAccess) + { + var source = $@"using System; +using NSubstitute; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {method}({call}, callInfo => 1, + callInfo => + {{ + {previousCallArgAccess} + return 1; + }}).AndDoes(callInfo => + {{ + {andDoesArgAccess} + }}); + }} + }} +}}"; + + await VerifyDiagnostic(source, Descriptor); + } + + public override async Task ReportsNoDiagnostics_When_AndDoesMethod_SetsDifferentArgument_AsPreviousSetupMethod(string method, string call, string andDoesArgAccess) + { + var source = $@"using System; +using NSubstitute; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {method}({call}, callInfo => + {{ + callInfo[0] = 1; + return 1; + }}).AndDoes(callInfo => + {{ + {andDoesArgAccess} + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + public override async Task ReportsNoDiagnostics_When_AndDoesMethod_AccessSameArguments_AsPreviousSetupMethod(string method, string call, string argAccess) + { + var source = $@"using System; +using NSubstitute; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {method}({call}, callInfo => + {{ + {argAccess} + return 1; + }}).AndDoes(callInfo => + {{ + {argAccess} + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + public override async Task ReportsNoDiagnostics_When_AndDoesMethod_SetSameArguments_AsPreviousSetupMethod_SetsIndirectly(string method) + { + var source = $@"using System; +using NSubstitute; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {method}(substitute.Bar(Arg.Any()), callInfo => + {{ + callInfo.Args()[0] = 1; + callInfo.ArgTypes()[0] = typeof(int); + ((byte[])callInfo[0])[0] = 1; + return 1; + }}).AndDoes(callInfo => + {{ + callInfo[0] = 1; + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + public override async Task ReportsNoDiagnostic_When_AndDoesMethod_SetArgument_AndPreviousMethod_IsNotUsedWithCallInfo(string method, string call, string andDoesArgAccess) + { + var source = $@"using System; +using NSubstitute; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {method}({call}, 1).AndDoes(callInfo => + {{ + {andDoesArgAccess} + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ConflictingArgumentAssignmentsAnalyzerTests/ThrowsAsExtensionMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ConflictingArgumentAssignmentsAnalyzerTests/ThrowsAsExtensionMethodTests.cs new file mode 100644 index 00000000..b25a9f76 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ConflictingArgumentAssignmentsAnalyzerTests/ThrowsAsExtensionMethodTests.cs @@ -0,0 +1,188 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.ConflictingArgumentAssignmentsAnalyzerTests +{ + [CombinatoryData("Throws", "ThrowsForAnyArgs")] + public class ThrowsAsExtensionMethodTests : ConflictingArgumentAssignmentsDiagnosticVerifier + { + public override async Task ReportsDiagnostic_When_AndDoesMethod_SetsSameArgument_AsPreviousSetupMethod(string method, string call, string previousCallArgAccess, string andDoesArgAccess) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {call}.{method}(callInfo => + {{ + {previousCallArgAccess} + return new Exception(); + }}).AndDoes(callInfo => + {{ + {andDoesArgAccess} + }}); + }} + }} +}}"; + + await VerifyDiagnostic(source, Descriptor); + } + + public override async Task ReportsNoDiagnostics_When_AndDoesMethod_SetsDifferentArgument_AsPreviousSetupMethod(string method, string call, string andDoesArgAccess) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {call}.{method}(callInfo => + {{ + callInfo[0] = 1; + return new Exception(); + }}).AndDoes(callInfo => + {{ + {andDoesArgAccess} + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + public override async Task ReportsNoDiagnostics_When_AndDoesMethod_AccessSameArguments_AsPreviousSetupMethod(string method, string call, string argAccess) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {call}.{method}(callInfo => + {{ + {argAccess} + return new Exception(); + }}).AndDoes(callInfo => + {{ + {argAccess} + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + public override async Task ReportsNoDiagnostics_When_AndDoesMethod_SetSameArguments_AsPreviousSetupMethod_SetsIndirectly(string method) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + substitute.Bar(Arg.Any()).{method}(callInfo => + {{ + callInfo.Args()[0] = 1; + callInfo.ArgTypes()[0] = typeof(int); + ((byte[])callInfo[0])[0] = 1; + return new Exception(); + }}).AndDoes(callInfo => + {{ + callInfo[0] = 1; + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + public override async Task ReportsNoDiagnostic_When_AndDoesMethod_SetArgument_AndPreviousMethod_IsNotUsedWithCallInfo(string method, string call, string andDoesArgAccess) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {call}.{method}(new Exception()).AndDoes(callInfo => + {{ + {andDoesArgAccess} + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ConflictingArgumentAssignmentsAnalyzerTests/ThrowsAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ConflictingArgumentAssignmentsAnalyzerTests/ThrowsAsOrdinaryMethodTests.cs new file mode 100644 index 00000000..e05de3f0 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/ConflictingArgumentAssignmentsAnalyzerTests/ThrowsAsOrdinaryMethodTests.cs @@ -0,0 +1,188 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.ConflictingArgumentAssignmentsAnalyzerTests +{ + [CombinatoryData("ExceptionExtensions.Throws", "ExceptionExtensions.ThrowsForAnyArgs")] + public class ThrowsAsOrdinaryMethodTests : ConflictingArgumentAssignmentsDiagnosticVerifier + { + public override async Task ReportsDiagnostic_When_AndDoesMethod_SetsSameArgument_AsPreviousSetupMethod(string method, string call, string previousCallArgAccess, string andDoesArgAccess) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {method}({call}, callInfo => + {{ + {previousCallArgAccess} + return new Exception(); + }}).AndDoes(callInfo => + {{ + {andDoesArgAccess} + }}); + }} + }} +}}"; + + await VerifyDiagnostic(source, Descriptor); + } + + public override async Task ReportsNoDiagnostics_When_AndDoesMethod_SetsDifferentArgument_AsPreviousSetupMethod(string method, string call, string andDoesArgAccess) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {method}({call}, callInfo => + {{ + callInfo[0] = 1; + return new Exception(); + }}).AndDoes(callInfo => + {{ + {andDoesArgAccess} + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + public override async Task ReportsNoDiagnostics_When_AndDoesMethod_AccessSameArguments_AsPreviousSetupMethod(string method, string call, string argAccess) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {method}({call}, callInfo => + {{ + {argAccess} + return new Exception(); + }}).AndDoes(callInfo => + {{ + {argAccess} + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + public override async Task ReportsNoDiagnostics_When_AndDoesMethod_SetSameArguments_AsPreviousSetupMethod_SetsIndirectly(string method) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {method}(substitute.Bar(Arg.Any()), callInfo => + {{ + callInfo.Args()[0] = 1; + callInfo.ArgTypes()[0] = typeof(int); + ((byte[])callInfo[0])[0] = 1; + return new Exception(); + }}).AndDoes(callInfo => + {{ + callInfo[0] = 1; + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + public override async Task ReportsNoDiagnostic_When_AndDoesMethod_SetArgument_AndPreviousMethod_IsNotUsedWithCallInfo(string method, string call, string andDoesArgAccess) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + int Bar(int x); + + int Barr {{ get; }} + + int this[int x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + {method}({call}, new Exception()).AndDoes(callInfo => + {{ + {andDoesArgAccess} + }}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + } +} \ No newline at end of file