diff --git a/src/NSubstitute.Analyzers.CSharp/CodeFixProviders/SyncOverAsyncThrowsCodeFixProvider.cs b/src/NSubstitute.Analyzers.CSharp/CodeFixProviders/SyncOverAsyncThrowsCodeFixProvider.cs index fc33719c..a1b2f234 100644 --- a/src/NSubstitute.Analyzers.CSharp/CodeFixProviders/SyncOverAsyncThrowsCodeFixProvider.cs +++ b/src/NSubstitute.Analyzers.CSharp/CodeFixProviders/SyncOverAsyncThrowsCodeFixProvider.cs @@ -2,6 +2,7 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using NSubstitute.Analyzers.Shared.CodeFixProviders; +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace NSubstitute.Analyzers.CSharp.CodeFixProviders; @@ -9,4 +10,13 @@ namespace NSubstitute.Analyzers.CSharp.CodeFixProviders; internal sealed class SyncOverAsyncThrowsCodeFixProvider : AbstractSyncOverAsyncThrowsCodeFixProvider { protected override SyntaxNode GetExpression(InvocationExpressionSyntax invocationExpressionSyntax) => ((MemberAccessExpressionSyntax)invocationExpressionSyntax.Expression).Expression; + + protected override SyntaxNode UpdateMemberExpression(InvocationExpressionSyntax invocationExpressionSyntax, SyntaxNode updatedNameSyntax) + { + var expressionSyntax = invocationExpressionSyntax.Expression; + return invocationExpressionSyntax.WithExpression(MemberAccessExpression( + expressionSyntax.Kind(), + ((MemberAccessExpressionSyntax)expressionSyntax).Expression, + (SimpleNameSyntax)updatedNameSyntax)); + } } \ No newline at end of file diff --git a/src/NSubstitute.Analyzers.Shared/CodeFixProviders/AbstractSyncOverAsyncThrowsCodeFixProvider.cs b/src/NSubstitute.Analyzers.Shared/CodeFixProviders/AbstractSyncOverAsyncThrowsCodeFixProvider.cs index de2d1713..976db029 100644 --- a/src/NSubstitute.Analyzers.Shared/CodeFixProviders/AbstractSyncOverAsyncThrowsCodeFixProvider.cs +++ b/src/NSubstitute.Analyzers.Shared/CodeFixProviders/AbstractSyncOverAsyncThrowsCodeFixProvider.cs @@ -39,19 +39,18 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken); var methodSymbol = (IMethodSymbol)semanticModel.GetSymbolInfo(invocation).Symbol; + var supportsThrowsAsync = SupportsThrowsAsync(semanticModel.Compilation); - if (methodSymbol.Parameters.Any(param => param.Type.IsCallInfoDelegate(semanticModel))) + if (!supportsThrowsAsync && methodSymbol.Parameters.Any(param => param.Type.IsCallInfoDelegate(semanticModel))) { return; } - var replacementMethod = methodSymbol.IsThrowsSyncForAnyArgsMethod() - ? "ReturnsForAnyArgs" - : "Returns"; + var replacementMethod = GetReplacementMethodName(methodSymbol, useModernSyntax: supportsThrowsAsync); var codeAction = CodeAction.Create( $"Replace with {replacementMethod}", - ct => CreateChangedDocument(context, semanticModel, invocation, methodSymbol, ct), + ct => CreateChangedDocument(context, semanticModel, invocation, methodSymbol, supportsThrowsAsync, ct), nameof(AbstractSyncOverAsyncThrowsCodeFixProvider)); context.RegisterCodeFix(codeAction, diagnostic); @@ -59,28 +58,56 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) protected abstract SyntaxNode GetExpression(TInvocationExpressionSyntax invocationExpressionSyntax); + protected abstract SyntaxNode UpdateMemberExpression(TInvocationExpressionSyntax invocationExpressionSyntax, SyntaxNode updatedNameSyntax); + private async Task CreateChangedDocument( CodeFixContext context, SemanticModel semanticModel, TInvocationExpressionSyntax currentInvocationExpression, IMethodSymbol invocationSymbol, + bool useModernSyntax, CancellationToken cancellationToken) { var documentEditor = await DocumentEditor.CreateAsync(context.Document, cancellationToken); var invocationOperation = (IInvocationOperation)semanticModel.GetOperation(currentInvocationExpression); - var updatedInvocationExpression = await CreateUpdatedInvocationExpression( - currentInvocationExpression, - invocationOperation, - invocationSymbol, - context); + var updatedInvocationExpression = useModernSyntax + ? await CreateThrowsAsyncInvocationExpression( + currentInvocationExpression, + invocationSymbol, + context) + : await CreateReturnInvocationExpression( + currentInvocationExpression, + invocationOperation, + invocationSymbol, + context); documentEditor.ReplaceNode(currentInvocationExpression, updatedInvocationExpression); return documentEditor.GetChangedDocument(); } - private async Task CreateUpdatedInvocationExpression( + private async Task CreateThrowsAsyncInvocationExpression( + TInvocationExpressionSyntax currentInvocationExpression, + IMethodSymbol invocationSymbol, + CodeFixContext context) + { + var updatedMethodName = + invocationSymbol.IsThrowsSyncForAnyArgsMethod() + ? MetadataNames.NSubstituteThrowsAsyncMethod + : MetadataNames.NSubstituteThrowsAsyncForAnyArgsMethod; + + var documentEditor = await DocumentEditor.CreateAsync(context.Document); + var syntaxGenerator = documentEditor.Generator; + + var nameSyntax = invocationSymbol.IsGenericMethod + ? syntaxGenerator.GenericName(updatedMethodName, invocationSymbol.TypeArguments) + : syntaxGenerator.IdentifierName(updatedMethodName); + + return UpdateMemberExpression(currentInvocationExpression, nameSyntax); + } + + private async Task CreateReturnInvocationExpression( TInvocationExpressionSyntax currentInvocationExpression, IInvocationOperation invocationOperation, IMethodSymbol invocationSymbol, @@ -93,11 +120,11 @@ private async Task CreateUpdatedInvocationExpression( CreateFromExceptionInvocationExpression(syntaxGenerator, invocationOperation); var returnsMethodName = - invocationSymbol.IsThrowsSyncForAnyArgsMethod() ? "ReturnsForAnyArgs" : "Returns"; + invocationSymbol.IsThrowsSyncForAnyArgsMethod() ? "Returns" : "ReturnsForAnyArgs"; if (invocationSymbol.MethodKind == MethodKind.Ordinary) { - return CreateUpdatedOrdinalInvocationExpression( + return CreateReturnOrdinalInvocationExpression( currentInvocationExpression, invocationOperation, syntaxGenerator, @@ -105,14 +132,14 @@ private async Task CreateUpdatedInvocationExpression( returnsMethodName); } - return CreateUpdatedExtensionInvocationExpression( + return CreateReturnExtensionInvocationExpression( currentInvocationExpression, syntaxGenerator, fromExceptionInvocationExpression, returnsMethodName); } - private static SyntaxNode CreateUpdatedOrdinalInvocationExpression( + private static SyntaxNode CreateReturnOrdinalInvocationExpression( TInvocationExpressionSyntax currentInvocationExpression, IInvocationOperation invocationOperation, SyntaxGenerator syntaxGenerator, @@ -126,7 +153,7 @@ private static SyntaxNode CreateUpdatedOrdinalInvocationExpression( fromExceptionInvocationExpression).WithTriviaFrom(currentInvocationExpression); } - private SyntaxNode CreateUpdatedExtensionInvocationExpression( + private SyntaxNode CreateReturnExtensionInvocationExpression( TInvocationExpressionSyntax currentInvocationExpression, SyntaxGenerator syntaxGenerator, SyntaxNode fromExceptionInvocationExpression, @@ -173,4 +200,26 @@ private static SyntaxNode GetExceptionCreationExpression( return invocationOperation.Arguments.OrderBy(arg => arg.Parameter.Ordinal) .First(arg => arg.Parameter.Ordinal > 0).Value.Syntax; } + + private static bool SupportsThrowsAsync(Compilation compilation) + { + var exceptionExtensionsTypeSymbol = compilation.GetTypeByMetadataName("NSubstitute.ExceptionExtensions.ExceptionExtensions"); + + return exceptionExtensionsTypeSymbol != null && + exceptionExtensionsTypeSymbol.GetMembers(MetadataNames.NSubstituteThrowsAsyncMethod).IsEmpty == false; + } + + private static string GetReplacementMethodName(IMethodSymbol methodSymbol, bool useModernSyntax) + { + if (useModernSyntax) + { + return methodSymbol.IsThrowsSyncForAnyArgsMethod() + ? MetadataNames.NSubstituteThrowsAsyncMethod + : MetadataNames.NSubstituteThrowsAsyncForAnyArgsMethod; + } + + return methodSymbol.IsThrowsSyncForAnyArgsMethod() + ? MetadataNames.NSubstituteReturnsMethod + : MetadataNames.NSubstituteReturnsForAnyArgsMethod; + } } \ No newline at end of file diff --git a/src/NSubstitute.Analyzers.Shared/Extensions/SubstituteSymbolExtensions.cs b/src/NSubstitute.Analyzers.Shared/Extensions/SubstituteSymbolExtensions.cs index b535a8cd..7bbfd1f5 100644 --- a/src/NSubstitute.Analyzers.Shared/Extensions/SubstituteSymbolExtensions.cs +++ b/src/NSubstitute.Analyzers.Shared/Extensions/SubstituteSymbolExtensions.cs @@ -51,7 +51,7 @@ public static bool IsThrowsSyncForAnyArgsMethod(this ISymbol symbol) { return IsMember( symbol, - MetadataNames.NSubstituteThrowsForAnyArgsMethod, + MetadataNames.NSubstituteThrowsMethod, MetadataNames.NSubstituteExceptionExtensionsFullTypeName); } diff --git a/src/NSubstitute.Analyzers.VisualBasic/CodeFixProviders/SyncOverAsyncThrowsCodeFixProvider.cs b/src/NSubstitute.Analyzers.VisualBasic/CodeFixProviders/SyncOverAsyncThrowsCodeFixProvider.cs index 8e50f23f..28323f7f 100644 --- a/src/NSubstitute.Analyzers.VisualBasic/CodeFixProviders/SyncOverAsyncThrowsCodeFixProvider.cs +++ b/src/NSubstitute.Analyzers.VisualBasic/CodeFixProviders/SyncOverAsyncThrowsCodeFixProvider.cs @@ -1,7 +1,9 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.VisualBasic; using Microsoft.CodeAnalysis.VisualBasic.Syntax; using NSubstitute.Analyzers.Shared.CodeFixProviders; +using static Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory; namespace NSubstitute.Analyzers.VisualBasic.CodeFixProviders; @@ -9,4 +11,14 @@ namespace NSubstitute.Analyzers.VisualBasic.CodeFixProviders; internal sealed class SyncOverAsyncThrowsCodeFixProvider : AbstractSyncOverAsyncThrowsCodeFixProvider { protected override SyntaxNode GetExpression(InvocationExpressionSyntax invocationExpressionSyntax) => ((MemberAccessExpressionSyntax)invocationExpressionSyntax.Expression).Expression; + + protected override SyntaxNode UpdateMemberExpression(InvocationExpressionSyntax invocationExpressionSyntax, SyntaxNode updatedNameSyntax) + { + var expressionSyntax = invocationExpressionSyntax.Expression; + return invocationExpressionSyntax.WithExpression(MemberAccessExpression( + expressionSyntax.Kind(), + ((MemberAccessExpressionSyntax)expressionSyntax).Expression, + Token(SyntaxKind.DotToken), + (SimpleNameSyntax)updatedNameSyntax)); + } } \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/SyncOverAsyncThrowsCodeFixActionsTests.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/SyncOverAsyncThrowsCodeFixActionsTests.cs index 6b6601db..0add03bd 100644 --- a/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/SyncOverAsyncThrowsCodeFixActionsTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/SyncOverAsyncThrowsCodeFixActionsTests.cs @@ -1,8 +1,10 @@ +using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using NSubstitute.Analyzers.CSharp.CodeFixProviders; using NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers; +using NSubstitute.Analyzers.Tests.Shared; using NSubstitute.Analyzers.Tests.Shared.CodeFixProviders; using Xunit; @@ -40,7 +42,38 @@ public void Test() }} }} }}"; - await VerifyCodeActions(source, expectedCodeActionTitle); + await VerifyCodeActions(source, NSubstituteVersion.NSubstitute4_2_2, expectedCodeActionTitle); + } + + [Theory] + [InlineData("Throws", "Replace with ThrowsAsync")] + [InlineData("ThrowsForAnyArgs", "Replace with ThrowsAsyncForAnyArgs")] + public async Task CreatesCodeAction_ForModernSyntax(string method, string expectedCodeActionTitle) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task Bar(); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + substitute.Bar().{method}(new Exception()); + substitute.Bar().{method}(callInfo => new Exception()); + substitute.Bar().{method}(createException: callInfo => new Exception()); + }} + }} +}}"; + await VerifyCodeActions(source, Enumerable.Repeat(expectedCodeActionTitle, 3).ToArray()); } [Theory] @@ -70,6 +103,6 @@ public void Test() }} }} }}"; - await VerifyCodeActions(source); + await VerifyCodeActions(source, NSubstituteVersion.NSubstitute4_2_2); } } \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/SyncOverAsyncThrowsCodeFixVerifier.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/SyncOverAsyncThrowsCodeFixVerifier.cs index 9027c553..c742c694 100644 --- a/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/SyncOverAsyncThrowsCodeFixVerifier.cs +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/SyncOverAsyncThrowsCodeFixVerifier.cs @@ -20,6 +20,15 @@ public static IEnumerable ThrowsTestCases } } + public static IEnumerable ThrowsAsyncTestCases + { + get + { + yield return new object[] { "Throws", "ThrowsAsync" }; + yield return new object[] { "ThrowsForAnyArgs", "ThrowsAsyncForAnyArgs" }; + } + } + protected override CodeFixProvider CodeFixProvider { get; } = new SyncOverAsyncThrowsCodeFixProvider(); protected override DiagnosticAnalyzer DiagnosticAnalyzer { get; } = new SyncOverAsyncThrowsAnalyzer(); @@ -35,4 +44,16 @@ public static IEnumerable ThrowsTestCases [Theory] [MemberData(nameof(ThrowsTestCases))] public abstract Task ReplacesThrowsWithReturns_WhenUsedInIndexer(string method, string updatedMethod); + + [Theory] + [MemberData(nameof(ThrowsAsyncTestCases))] + public abstract Task ReplacesThrowsWithThrowsAsync_WhenUsedInMethod(string method, string updatedMethod); + + [Theory] + [MemberData(nameof(ThrowsAsyncTestCases))] + public abstract Task ReplacesThrowsWithThrowsAsync_WhenUsedInProperty(string method, string updatedMethod); + + [Theory] + [MemberData(nameof(ThrowsAsyncTestCases))] + public abstract Task ReplacesThrowsWithThrowsAsync_WhenUsedInIndexer(string method, string updatedMethod); } \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsExtensionMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsExtensionMethodTests.cs index 0d06b40c..7b8e27da 100644 --- a/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsExtensionMethodTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsExtensionMethodTests.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared; namespace NSubstitute.Analyzers.Tests.CSharp.CodeFixProviderTests.SyncOverAsyncThrowsCodeFixProviderTests; @@ -52,7 +53,7 @@ public void Test() }} }}"; - await VerifyFix(source, newSource); + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); } public override async Task ReplacesThrowsWithReturns_WhenUsedInProperty(string method, string updatedMethod) @@ -103,7 +104,7 @@ public void Test() }} }}"; - await VerifyFix(source, newSource); + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); } public override async Task ReplacesThrowsWithReturns_WhenUsedInIndexer(string method, string updatedMethod) @@ -154,6 +155,183 @@ public void Test() }} }}"; + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInMethod(string method, string updatedMethod) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task Bar(); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + substitute.Bar().{method}(new Exception()); + substitute.Bar().{method}(ex: new Exception()); + substitute.Bar().{method}(callInfo => new Exception()); + substitute.Bar().{method}(createException: callInfo => new Exception()); + substitute.Bar().{method}(callInfo => {{ return new Exception(); }}); + substitute.Bar().{method}(createException: callInfo => {{ return new Exception(); }}); + }} + }} +}}"; + + var newSource = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task Bar(); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + substitute.Bar().{updatedMethod}(new Exception()); + substitute.Bar().{updatedMethod}(ex: new Exception()); + substitute.Bar().{updatedMethod}(callInfo => new Exception()); + substitute.Bar().{updatedMethod}(createException: callInfo => new Exception()); + substitute.Bar().{updatedMethod}(callInfo => {{ return new Exception(); }}); + substitute.Bar().{updatedMethod}(createException: callInfo => {{ return new Exception(); }}); + }} + }} +}}"; + + await VerifyFix(source, newSource); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInProperty(string method, string updatedMethod) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task Bar {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + substitute.Bar.{method}(new Exception()); + substitute.Bar.{method}(ex: new Exception()); + substitute.Bar.{method}(callInfo => new Exception()); + substitute.Bar.{method}(createException: callInfo => new Exception()); + substitute.Bar.{method}(callInfo => {{ return new Exception(); }}); + substitute.Bar.{method}(createException: callInfo => {{ return new Exception(); }}); + }} + }} +}}"; + + var newSource = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task Bar {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + substitute.Bar.{updatedMethod}(new Exception()); + substitute.Bar.{updatedMethod}(ex: new Exception()); + substitute.Bar.{updatedMethod}(callInfo => new Exception()); + substitute.Bar.{updatedMethod}(createException: callInfo => new Exception()); + substitute.Bar.{updatedMethod}(callInfo => {{ return new Exception(); }}); + substitute.Bar.{updatedMethod}(createException: callInfo => {{ return new Exception(); }}); + }} + }} +}}"; + + await VerifyFix(source, newSource); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInIndexer(string method, string updatedMethod) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task this[int x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + substitute[0].{method}(new Exception()); + substitute[0].{method}(ex: new Exception()); + substitute[0].{method}(callInfo => new Exception()); + substitute[0].{method}(createException: callInfo => new Exception()); + substitute[0].{method}(callInfo => {{ return new Exception(); }}); + substitute[0].{method}(createException: callInfo => {{ return new Exception(); }}); + }} + }} +}}"; + + var newSource = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task this[int x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + substitute[0].{updatedMethod}(new Exception()); + substitute[0].{updatedMethod}(ex: new Exception()); + substitute[0].{updatedMethod}(callInfo => new Exception()); + substitute[0].{updatedMethod}(createException: callInfo => new Exception()); + substitute[0].{updatedMethod}(callInfo => {{ return new Exception(); }}); + substitute[0].{updatedMethod}(createException: callInfo => {{ return new Exception(); }}); + }} + }} +}}"; + await VerifyFix(source, newSource); } } \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsExtensionsMethodWithGenericTypeSpecifiedTests.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsExtensionsMethodWithGenericTypeSpecifiedTests.cs index 26a55428..62e96d9f 100644 --- a/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsExtensionsMethodWithGenericTypeSpecifiedTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsExtensionsMethodWithGenericTypeSpecifiedTests.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared; namespace NSubstitute.Analyzers.Tests.CSharp.CodeFixProviderTests.SyncOverAsyncThrowsCodeFixProviderTests; @@ -50,7 +51,7 @@ public void Test() }} }}"; - await VerifyFix(source, newSource); + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); } public override async Task ReplacesThrowsWithReturns_WhenUsedInProperty(string method, string updatedMethod) @@ -99,7 +100,7 @@ public void Test() }} }}"; - await VerifyFix(source, newSource); + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); } public override async Task ReplacesThrowsWithReturns_WhenUsedInIndexer(string method, string updatedMethod) @@ -148,6 +149,153 @@ public void Test() }} }}"; + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInMethod(string method, string updatedMethod) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task Bar(); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + substitute.Bar().{method}(); + }} + }} +}}"; + + var newSource = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task Bar(); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + substitute.Bar().{updatedMethod}(); + }} + }} +}}"; + + await VerifyFix(source, newSource); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInProperty(string method, string updatedMethod) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task Bar {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + substitute.Bar.{method}(); + }} + }} +}}"; + + var newSource = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task Bar {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + substitute.Bar.{updatedMethod}(); + }} + }} +}}"; + + await VerifyFix(source, newSource); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInIndexer(string method, string updatedMethod) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task this[int x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + substitute[0].{method}(); + }} + }} +}}"; + + var newSource = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task this[int x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + substitute[0].{updatedMethod}(); + }} + }} +}}"; + await VerifyFix(source, newSource); } } \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsOrdinaryMethodTests.cs index 1e3039e3..43733a51 100644 --- a/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsOrdinaryMethodTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsOrdinaryMethodTests.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared; namespace NSubstitute.Analyzers.Tests.CSharp.CodeFixProviderTests.SyncOverAsyncThrowsCodeFixProviderTests; @@ -54,7 +55,7 @@ public void Test() }} }}"; - await VerifyFix(source, newSource); + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); } public override async Task ReplacesThrowsWithReturns_WhenUsedInProperty(string method, string updatedMethod) @@ -107,7 +108,7 @@ public void Test() }} }}"; - await VerifyFix(source, newSource); + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); } public override async Task ReplacesThrowsWithReturns_WhenUsedInIndexer(string method, string updatedMethod) @@ -160,6 +161,201 @@ public void Test() }} }}"; + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInMethod(string method, string updatedMethod) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task Bar(); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + ExceptionExtensions.{method}(substitute.Bar(), new Exception()); + ExceptionExtensions.{method}(value: substitute.Bar(), ex: new Exception()); + ExceptionExtensions.{method}(ex: new Exception(), value: substitute.Bar()); + ExceptionExtensions.{method}(substitute.Bar(), callInfo => new Exception()); + ExceptionExtensions.{method}(value: substitute.Bar(), createException: callInfo => new Exception()); + ExceptionExtensions.{method}(createException: callInfo => new Exception(), value: substitute.Bar()); + ExceptionExtensions.{method}(substitute.Bar(), callInfo => {{ return new Exception(); }}); + ExceptionExtensions.{method}(value: substitute.Bar(), createException: callInfo => {{ return new Exception(); }}); + ExceptionExtensions.{method}(createException: callInfo => {{ return new Exception(); }}, value: substitute.Bar()); + }} + }} +}}"; + + var newSource = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task Bar(); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + ExceptionExtensions.{updatedMethod}(substitute.Bar(), new Exception()); + ExceptionExtensions.{updatedMethod}(value: substitute.Bar(), ex: new Exception()); + ExceptionExtensions.{updatedMethod}(ex: new Exception(), value: substitute.Bar()); + ExceptionExtensions.{updatedMethod}(substitute.Bar(), callInfo => new Exception()); + ExceptionExtensions.{updatedMethod}(value: substitute.Bar(), createException: callInfo => new Exception()); + ExceptionExtensions.{updatedMethod}(createException: callInfo => new Exception(), value: substitute.Bar()); + ExceptionExtensions.{updatedMethod}(substitute.Bar(), callInfo => {{ return new Exception(); }}); + ExceptionExtensions.{updatedMethod}(value: substitute.Bar(), createException: callInfo => {{ return new Exception(); }}); + ExceptionExtensions.{updatedMethod}(createException: callInfo => {{ return new Exception(); }}, value: substitute.Bar()); + }} + }} +}}"; + + await VerifyFix(source, newSource); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInProperty(string method, string updatedMethod) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task Bar {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + ExceptionExtensions.{method}(substitute.Bar, new Exception()); + ExceptionExtensions.{method}(value: substitute.Bar, ex: new Exception()); + ExceptionExtensions.{method}(ex: new Exception(), value: substitute.Bar); + ExceptionExtensions.{method}(substitute.Bar, callInfo => new Exception()); + ExceptionExtensions.{method}(value: substitute.Bar, createException: callInfo => new Exception()); + ExceptionExtensions.{method}(createException: callInfo => new Exception(), value: substitute.Bar); + ExceptionExtensions.{method}(substitute.Bar, callInfo => {{ return new Exception(); }}); + ExceptionExtensions.{method}(value: substitute.Bar, createException: callInfo => {{ return new Exception(); }}); + ExceptionExtensions.{method}(createException: callInfo => {{ return new Exception(); }}, value: substitute.Bar); + }} + }} +}}"; + + var newSource = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task Bar {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + ExceptionExtensions.{updatedMethod}(substitute.Bar, new Exception()); + ExceptionExtensions.{updatedMethod}(value: substitute.Bar, ex: new Exception()); + ExceptionExtensions.{updatedMethod}(ex: new Exception(), value: substitute.Bar); + ExceptionExtensions.{updatedMethod}(substitute.Bar, callInfo => new Exception()); + ExceptionExtensions.{updatedMethod}(value: substitute.Bar, createException: callInfo => new Exception()); + ExceptionExtensions.{updatedMethod}(createException: callInfo => new Exception(), value: substitute.Bar); + ExceptionExtensions.{updatedMethod}(substitute.Bar, callInfo => {{ return new Exception(); }}); + ExceptionExtensions.{updatedMethod}(value: substitute.Bar, createException: callInfo => {{ return new Exception(); }}); + ExceptionExtensions.{updatedMethod}(createException: callInfo => {{ return new Exception(); }}, value: substitute.Bar); + }} + }} +}}"; + + await VerifyFix(source, newSource); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInIndexer(string method, string updatedMethod) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task this[int x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + ExceptionExtensions.{method}(substitute[0], new Exception()); + ExceptionExtensions.{method}(value: substitute[0], ex: new Exception()); + ExceptionExtensions.{method}(ex: new Exception(), value: substitute[0]); + ExceptionExtensions.{method}(substitute[0], callInfo => new Exception()); + ExceptionExtensions.{method}(value: substitute[0], createException: callInfo => new Exception()); + ExceptionExtensions.{method}(createException: callInfo => new Exception(), value: substitute[0]); + ExceptionExtensions.{method}(substitute[0], callInfo => {{ return new Exception(); }}); + ExceptionExtensions.{method}(value: substitute[0], createException: callInfo => {{ return new Exception(); }}); + ExceptionExtensions.{method}(createException: callInfo => {{ return new Exception(); }}, value: substitute[0]); + }} + }} +}}"; + + var newSource = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task this[int x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + ExceptionExtensions.{updatedMethod}(substitute[0], new Exception()); + ExceptionExtensions.{updatedMethod}(value: substitute[0], ex: new Exception()); + ExceptionExtensions.{updatedMethod}(ex: new Exception(), value: substitute[0]); + ExceptionExtensions.{updatedMethod}(substitute[0], callInfo => new Exception()); + ExceptionExtensions.{updatedMethod}(value: substitute[0], createException: callInfo => new Exception()); + ExceptionExtensions.{updatedMethod}(createException: callInfo => new Exception(), value: substitute[0]); + ExceptionExtensions.{updatedMethod}(substitute[0], callInfo => {{ return new Exception(); }}); + ExceptionExtensions.{updatedMethod}(value: substitute[0], createException: callInfo => {{ return new Exception(); }}); + ExceptionExtensions.{updatedMethod}(createException: callInfo => {{ return new Exception(); }}, value: substitute[0]); + }} + }} +}}"; + await VerifyFix(source, newSource); } } \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs index 1f82bed5..17a7d2d3 100644 --- a/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/CodeFixProviderTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared; namespace NSubstitute.Analyzers.Tests.CSharp.CodeFixProviderTests.SyncOverAsyncThrowsCodeFixProviderTests; @@ -52,7 +53,7 @@ public void Test() }} }}"; - await VerifyFix(source, newSource); + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); } public override async Task ReplacesThrowsWithReturns_WhenUsedInProperty(string method, string updatedMethod) @@ -103,7 +104,7 @@ public void Test() }} }}"; - await VerifyFix(source, newSource); + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); } public override async Task ReplacesThrowsWithReturns_WhenUsedInIndexer(string method, string updatedMethod) @@ -154,6 +155,159 @@ public void Test() }} }}"; + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInMethod(string method, string updatedMethod) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task Bar(); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + ExceptionExtensions.{method}(substitute.Bar()); + ExceptionExtensions.{method}(value: substitute.Bar()); + }} + }} +}}"; + + var newSource = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task Bar(); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + ExceptionExtensions.{updatedMethod}(substitute.Bar()); + ExceptionExtensions.{updatedMethod}(value: substitute.Bar()); + }} + }} +}}"; + + await VerifyFix(source, newSource); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInProperty(string method, string updatedMethod) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task Bar {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + ExceptionExtensions.{method}(substitute.Bar); + ExceptionExtensions.{method}(value: substitute.Bar); + }} + }} +}}"; + + var newSource = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task Bar {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + ExceptionExtensions.{updatedMethod}(substitute.Bar); + ExceptionExtensions.{updatedMethod}(value: substitute.Bar); + }} + }} +}}"; + + await VerifyFix(source, newSource); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInIndexer(string method, string updatedMethod) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task this[int x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + ExceptionExtensions.{method}(substitute[0]); + ExceptionExtensions.{method}(value: substitute[0]); + }} + }} +}}"; + + var newSource = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Task this[int x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = NSubstitute.Substitute.For(); + ExceptionExtensions.{updatedMethod}(substitute[0]); + ExceptionExtensions.{updatedMethod}(value: substitute[0]); + }} + }} +}}"; + await VerifyFix(source, newSource); } } \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.Shared/CodeFixProviders/CodeFixCodeActionsVerifier.cs b/tests/NSubstitute.Analyzers.Tests.Shared/CodeFixProviders/CodeFixCodeActionsVerifier.cs index 141c2cf1..2cda1ede 100644 --- a/tests/NSubstitute.Analyzers.Tests.Shared/CodeFixProviders/CodeFixCodeActionsVerifier.cs +++ b/tests/NSubstitute.Analyzers.Tests.Shared/CodeFixProviders/CodeFixCodeActionsVerifier.cs @@ -26,42 +26,46 @@ protected CodeFixCodeActionsVerifier(WorkspaceFactory workspaceFactory) protected override string AnalyzerSettings { get; } = Json.Encode(new object()); - protected async Task VerifyCodeActions(string source, params string[] expectedCodeActionTitles) + protected async Task VerifyCodeActions(string source, NSubstituteVersion version, params string[] expectedCodeActionTitles) { - var codeActions = await RegisterCodeFixes(source); + var codeActions = await RegisterCodeFixes(source, version); codeActions.Should().NotBeNull(); codeActions.Select(action => action.Title).Should().BeEquivalentTo(expectedCodeActionTitles ?? Array.Empty()); } - private async Task> RegisterCodeFixes(string source) + protected Task VerifyCodeActions(string source, params string[] expectedCodeActionTitles) { - using (var workspace = new AdhocWorkspace()) - { - var actions = new List(); - var project = AddProject(workspace.CurrentSolution, source); + return VerifyCodeActions(source, NSubstituteVersion.Latest, expectedCodeActionTitles); + } - var document = project.Documents.Single(); + private async Task> RegisterCodeFixes(string source, NSubstituteVersion version) + { + using var workspace = new AdhocWorkspace(); + var actions = new List(); + var project = AddProject(workspace.CurrentSolution, source); + project = UpdateNSubstituteMetadataReference(project, version); - var compilation = await document.Project.GetCompilationAsync(); - var compilationDiagnostics = compilation.GetDiagnostics(); + var document = project.Documents.Single(); - VerifyNoCompilerDiagnosticErrors(compilationDiagnostics); + var compilation = await document.Project.GetCompilationAsync(); + var compilationDiagnostics = compilation.GetDiagnostics(); - var analyzerDiagnostics = await compilation.GetSortedAnalyzerDiagnostics( - DiagnosticAnalyzer, - project.AnalyzerOptions); + VerifyNoCompilerDiagnosticErrors(compilationDiagnostics); - foreach (var context in analyzerDiagnostics.Select(diagnostic => new CodeFixContext( - document, - analyzerDiagnostics[0], - (action, array) => actions.Add(action), - CancellationToken.None))) - { - await CodeFixProvider.RegisterCodeFixesAsync(context); - } + var analyzerDiagnostics = await compilation.GetSortedAnalyzerDiagnostics( + DiagnosticAnalyzer, + project.AnalyzerOptions); - return actions; + foreach (var context in analyzerDiagnostics.Select(diagnostic => new CodeFixContext( + document, + analyzerDiagnostics[0], + (action, array) => actions.Add(action), + CancellationToken.None))) + { + await CodeFixProvider.RegisterCodeFixesAsync(context); } + + return actions; } } \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.Shared/CodeFixProviders/CodeFixVerifier.cs b/tests/NSubstitute.Analyzers.Tests.Shared/CodeFixProviders/CodeFixVerifier.cs index bcc7ddbe..4f24adf0 100644 --- a/tests/NSubstitute.Analyzers.Tests.Shared/CodeFixProviders/CodeFixVerifier.cs +++ b/tests/NSubstitute.Analyzers.Tests.Shared/CodeFixProviders/CodeFixVerifier.cs @@ -24,86 +24,71 @@ protected CodeFixVerifier(WorkspaceFactory workspaceFactory) protected abstract DiagnosticAnalyzer DiagnosticAnalyzer { get; } + protected Task VerifyFix( + string oldSource, + string newSource, + NSubstituteVersion version) => VerifyFix(oldSource, newSource, null, version); + protected async Task VerifyFix( string oldSource, string newSource, int? codeFixIndex = null, NSubstituteVersion version = NSubstituteVersion.Latest) { - using (var workspace = new AdhocWorkspace()) - { - var project = AddProject(workspace.CurrentSolution, oldSource); - - project = UpdateNSubstituteMetadataReference(project, version); + using var workspace = new AdhocWorkspace(); + var project = AddProject(workspace.CurrentSolution, oldSource); - var document = project.Documents.Single(); - var compilation = await project.GetCompilationAsync(); + project = UpdateNSubstituteMetadataReference(project, version); - var compilerDiagnostics = compilation.GetDiagnostics(); + var document = project.Documents.Single(); + var compilation = await project.GetCompilationAsync(); - VerifyNoCompilerDiagnosticErrors(compilerDiagnostics); + var compilerDiagnostics = compilation.GetDiagnostics(); - var analyzerDiagnostics = await compilation.GetSortedAnalyzerDiagnostics( - DiagnosticAnalyzer, - project.AnalyzerOptions); + VerifyNoCompilerDiagnosticErrors(compilerDiagnostics); - var previousAnalyzerDiagnostics = analyzerDiagnostics; - var attempts = analyzerDiagnostics.Length; + var analyzerDiagnostics = await compilation.GetSortedAnalyzerDiagnostics( + DiagnosticAnalyzer, + project.AnalyzerOptions); - for (var i = 0; i < attempts; ++i) - { - var actions = new List(); - var context = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a), CancellationToken.None); - await CodeFixProvider.RegisterCodeFixesAsync(context); + var previousAnalyzerDiagnostics = analyzerDiagnostics; + var attempts = analyzerDiagnostics.Length; - if (!actions.Any()) - { - break; - } + for (var i = 0; i < attempts; ++i) + { + var actions = new List(); + var context = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a), CancellationToken.None); + await CodeFixProvider.RegisterCodeFixesAsync(context); - document = await document.ApplyCodeAction(actions[codeFixIndex ?? 0]); - compilation = await document.Project.GetCompilationAsync(); + if (!actions.Any()) + { + break; + } - compilerDiagnostics = compilation.GetDiagnostics(); + document = await document.ApplyCodeAction(actions[codeFixIndex ?? 0]); + compilation = await document.Project.GetCompilationAsync(); - VerifyNoCompilerDiagnosticErrors(compilerDiagnostics); + compilerDiagnostics = compilation.GetDiagnostics(); - analyzerDiagnostics = await compilation.GetSortedAnalyzerDiagnostics( - DiagnosticAnalyzer, - project.AnalyzerOptions); + VerifyNoCompilerDiagnosticErrors(compilerDiagnostics); - // check if there are analyzer diagnostics left after the code fix - var newAnalyzerDiagnostics = analyzerDiagnostics.Except(previousAnalyzerDiagnostics).ToList(); - if (analyzerDiagnostics.Length == previousAnalyzerDiagnostics.Length && newAnalyzerDiagnostics.Any()) - { - Execute.Assertion.Fail( - $"Fix didn't fix analyzer diagnostics: {newAnalyzerDiagnostics.ToDebugString()} New document:{Environment.NewLine}{await document.ToFullString()}"); - } + analyzerDiagnostics = await compilation.GetSortedAnalyzerDiagnostics( + DiagnosticAnalyzer, + project.AnalyzerOptions); - previousAnalyzerDiagnostics = analyzerDiagnostics; + // check if there are analyzer diagnostics left after the code fix + var newAnalyzerDiagnostics = analyzerDiagnostics.Except(previousAnalyzerDiagnostics).ToList(); + if (analyzerDiagnostics.Length == previousAnalyzerDiagnostics.Length && newAnalyzerDiagnostics.Any()) + { + Execute.Assertion.Fail( + $"Fix didn't fix analyzer diagnostics: {newAnalyzerDiagnostics.ToDebugString()} New document:{Environment.NewLine}{await document.ToFullString()}"); } - var actual = await document.ToFullString(); - - actual.Should().Be(newSource); - } - } - - private static Project UpdateNSubstituteMetadataReference(Project project, NSubstituteVersion version) - { - if (version == NSubstituteVersion.Latest) - { - return project; + previousAnalyzerDiagnostics = analyzerDiagnostics; } - project = project.RemoveMetadataReference(RuntimeMetadataReference.NSubstituteLatestReference); - - project = version switch - { - NSubstituteVersion.NSubstitute4_2_2 => project.AddMetadataReference(RuntimeMetadataReference.NSubstitute422Reference), - _ => throw new ArgumentException($"NSubstitute {version} is not supported", nameof(version)) - }; + var actual = await document.ToFullString(); - return project; + actual.Should().Be(newSource); } } \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.Shared/CodeFixProviders/ISyncOverAsyncThrowsCodeFixActionsVerifier.cs b/tests/NSubstitute.Analyzers.Tests.Shared/CodeFixProviders/ISyncOverAsyncThrowsCodeFixActionsVerifier.cs index bb00f09f..a9654bb4 100644 --- a/tests/NSubstitute.Analyzers.Tests.Shared/CodeFixProviders/ISyncOverAsyncThrowsCodeFixActionsVerifier.cs +++ b/tests/NSubstitute.Analyzers.Tests.Shared/CodeFixProviders/ISyncOverAsyncThrowsCodeFixActionsVerifier.cs @@ -6,5 +6,7 @@ public interface ISyncOverAsyncThrowsCodeFixActionsVerifier { Task CreatesCodeAction_WhenOverloadSupported(string method, string expectedCodeActionTitle); + Task CreatesCodeAction_ForModernSyntax(string method, string expectedCodeActionTitle); + Task DoesNotCreateCodeAction_WhenOverloadNotSupported(string method); } \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.Shared/CodeFixProviders/ISyncOverAsyncThrowsCodeFixVerifier.cs b/tests/NSubstitute.Analyzers.Tests.Shared/CodeFixProviders/ISyncOverAsyncThrowsCodeFixVerifier.cs index e0201067..9290be9e 100644 --- a/tests/NSubstitute.Analyzers.Tests.Shared/CodeFixProviders/ISyncOverAsyncThrowsCodeFixVerifier.cs +++ b/tests/NSubstitute.Analyzers.Tests.Shared/CodeFixProviders/ISyncOverAsyncThrowsCodeFixVerifier.cs @@ -9,4 +9,10 @@ public interface ISyncOverAsyncThrowsCodeFixVerifier Task ReplacesThrowsWithReturns_WhenUsedInProperty(string method, string updatedMethod); Task ReplacesThrowsWithReturns_WhenUsedInIndexer(string method, string updatedMethod); + + Task ReplacesThrowsWithThrowsAsync_WhenUsedInMethod(string method, string updatedMethod); + + Task ReplacesThrowsWithThrowsAsync_WhenUsedInProperty(string method, string updatedMethod); + + Task ReplacesThrowsWithThrowsAsync_WhenUsedInIndexer(string method, string updatedMethod); } \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.Shared/CodeVerifier.cs b/tests/NSubstitute.Analyzers.Tests.Shared/CodeVerifier.cs index 0d598be0..de485b4f 100644 --- a/tests/NSubstitute.Analyzers.Tests.Shared/CodeVerifier.cs +++ b/tests/NSubstitute.Analyzers.Tests.Shared/CodeVerifier.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Immutable; using System.Globalization; using System.Linq; @@ -45,4 +46,22 @@ protected static void VerifyNoCompilerDiagnosticErrors(ImmutableArray project.AddMetadataReference(RuntimeMetadataReference.NSubstitute422Reference), + _ => throw new ArgumentException($"NSubstitute {version} is not supported", nameof(version)) + }; + + return project; + } } \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/SyncOverAsyncThrowsCodeFixActionsTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/SyncOverAsyncThrowsCodeFixActionsTests.cs index 2b77e72e..37082437 100644 --- a/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/SyncOverAsyncThrowsCodeFixActionsTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/SyncOverAsyncThrowsCodeFixActionsTests.cs @@ -1,6 +1,8 @@ +using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; +using NSubstitute.Analyzers.Tests.Shared; using NSubstitute.Analyzers.Tests.Shared.CodeFixProviders; using NSubstitute.Analyzers.VisualBasic.CodeFixProviders; using NSubstitute.Analyzers.VisualBasic.DiagnosticAnalyzers; @@ -40,6 +42,34 @@ End Class await VerifyCodeActions(source, expectedCodeActionTitle); } + [Theory] + [InlineData("Throws", "Replace with ThrowsAsync")] + [InlineData("ThrowsForAnyArgs", "Replace with ThrowsAsyncForAnyArgs")] + public async Task CreatesCodeAction_ForModernSyntax(string method, string expectedCodeActionTitle) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface IFoo + Function Bar() As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.Bar().{method}(New Exception()) + substitute.Bar().{method}(Function(callInfo) New Exception()) + substitute.Bar().{method}(createException:= Function(callInfo) New Exception()) + End Sub + End Class +End Namespace"; + + await VerifyCodeActions(source, Enumerable.Repeat(expectedCodeActionTitle, 3).ToArray()); + } + [Theory] [InlineData("Throws")] [InlineData("ThrowsForAnyArgs")] @@ -64,6 +94,6 @@ End Sub End Class End Namespace"; - await VerifyCodeActions(source); + await VerifyCodeActions(source, NSubstituteVersion.NSubstitute4_2_2); } } \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/SyncOverAsyncThrowsCodeFixVerifier.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/SyncOverAsyncThrowsCodeFixVerifier.cs index 35a0fcec..bbc1559e 100644 --- a/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/SyncOverAsyncThrowsCodeFixVerifier.cs +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/SyncOverAsyncThrowsCodeFixVerifier.cs @@ -20,6 +20,15 @@ public static IEnumerable ThrowsTestCases } } + public static IEnumerable ThrowsAsyncTestCases + { + get + { + yield return new object[] { "Throws", "ThrowsAsync" }; + yield return new object[] { "ThrowsForAnyArgs", "ThrowsAsyncForAnyArgs" }; + } + } + protected override CodeFixProvider CodeFixProvider { get; } = new SyncOverAsyncThrowsCodeFixProvider(); protected override DiagnosticAnalyzer DiagnosticAnalyzer { get; } = new SyncOverAsyncThrowsAnalyzer(); @@ -35,4 +44,16 @@ public static IEnumerable ThrowsTestCases [Theory] [MemberData(nameof(ThrowsTestCases))] public abstract Task ReplacesThrowsWithReturns_WhenUsedInIndexer(string method, string updatedMethod); + + [Theory] + [MemberData(nameof(ThrowsAsyncTestCases))] + public abstract Task ReplacesThrowsWithThrowsAsync_WhenUsedInMethod(string method, string updatedMethod); + + [Theory] + [MemberData(nameof(ThrowsAsyncTestCases))] + public abstract Task ReplacesThrowsWithThrowsAsync_WhenUsedInProperty(string method, string updatedMethod); + + [Theory] + [MemberData(nameof(ThrowsAsyncTestCases))] + public abstract Task ReplacesThrowsWithThrowsAsync_WhenUsedInIndexer(string method, string updatedMethod); } \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsExtensionMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsExtensionMethodTests.cs index 536ffa4a..1c0f30b4 100644 --- a/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsExtensionMethodTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsExtensionMethodTests.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared; namespace NSubstitute.Analyzers.Tests.VisualBasic.CodeFixProvidersTests.SyncOverAsyncThrowsCodeFixProviderTests; @@ -44,7 +45,7 @@ End Sub End Class End Namespace"; - await VerifyFix(source, newSource); + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); } public override async Task ReplacesThrowsWithReturns_WhenUsedInProperty(string method, string updatedMethod) @@ -87,7 +88,7 @@ End Sub End Class End Namespace"; - await VerifyFix(source, newSource); + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); } public override async Task ReplacesThrowsWithReturns_WhenUsedInIndexer(string method, string updatedMethod) @@ -130,6 +131,201 @@ End Sub End Class End Namespace"; + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInMethod(string method, string updatedMethod) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface IFoo + Function Bar() As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.Bar().{method}(New Exception()) + substitute.Bar().{method}(ex := New Exception()) + substitute.Bar().{method}(Function(callInfo) New Exception()) + substitute.Bar().{method}(createException:= Function(callInfo) New Exception()) + substitute.Bar().{method}( + Function(callInfo) + Return New Exception() + End Function) + substitute.Bar().{method}( + createException := + Function(callInfo) + Return New Exception() + End Function) + End Sub + End Class +End Namespace"; + + var newSource = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface IFoo + Function Bar() As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.Bar().{updatedMethod}(New Exception()) + substitute.Bar().{updatedMethod}(ex := New Exception()) + substitute.Bar().{updatedMethod}(Function(callInfo) New Exception()) + substitute.Bar().{updatedMethod}(createException:= Function(callInfo) New Exception()) + substitute.Bar().{updatedMethod}( + Function(callInfo) + Return New Exception() + End Function) + substitute.Bar().{updatedMethod}( + createException := + Function(callInfo) + Return New Exception() + End Function) + End Sub + End Class +End Namespace"; + + await VerifyFix(source, newSource); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInProperty(string method, string updatedMethod) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface IFoo + Property Bar As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.Bar.{method}(New Exception()) + substitute.Bar.{method}(ex := New Exception()) + substitute.Bar.{method}(Function(callInfo) New Exception()) + substitute.Bar.{method}(createException:= Function(callInfo) New Exception()) + substitute.Bar.{method}( + Function(callInfo) + Return New Exception() + End Function) + substitute.Bar.{method}( + createException := + Function(callInfo) + Return New Exception() + End Function) + End Sub + End Class +End Namespace"; + + var newSource = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface IFoo + Property Bar As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.Bar.{updatedMethod}(New Exception()) + substitute.Bar.{updatedMethod}(ex := New Exception()) + substitute.Bar.{updatedMethod}(Function(callInfo) New Exception()) + substitute.Bar.{updatedMethod}(createException:= Function(callInfo) New Exception()) + substitute.Bar.{updatedMethod}( + Function(callInfo) + Return New Exception() + End Function) + substitute.Bar.{updatedMethod}( + createException := + Function(callInfo) + Return New Exception() + End Function) + End Sub + End Class +End Namespace"; + + await VerifyFix(source, newSource); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInIndexer(string method, string updatedMethod) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface IFoo + Default Property Item(ByVal x As Integer) As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute(0).{method}(New Exception()) + substitute(0).{method}(ex := New Exception()) + substitute(0).{method}(Function(callInfo) New Exception()) + substitute(0).{method}(createException:= Function(callInfo) New Exception()) + substitute(0).{method}( + Function(callInfo) + Return New Exception() + End Function) + substitute(0).{method}( + createException := + Function(callInfo) + Return New Exception() + End Function) + End Sub + End Class +End Namespace"; + + var newSource = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface IFoo + Default Property Item(ByVal x As Integer) As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute(0).{updatedMethod}(New Exception()) + substitute(0).{updatedMethod}(ex := New Exception()) + substitute(0).{updatedMethod}(Function(callInfo) New Exception()) + substitute(0).{updatedMethod}(createException:= Function(callInfo) New Exception()) + substitute(0).{updatedMethod}( + Function(callInfo) + Return New Exception() + End Function) + substitute(0).{updatedMethod}( + createException := + Function(callInfo) + Return New Exception() + End Function) + End Sub + End Class +End Namespace"; + await VerifyFix(source, newSource); } } \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsExtensionsMethodWithGenericTypeSpecifiedTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsExtensionsMethodWithGenericTypeSpecifiedTests.cs index 46a98913..67c4c597 100644 --- a/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsExtensionsMethodWithGenericTypeSpecifiedTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsExtensionsMethodWithGenericTypeSpecifiedTests.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared; namespace NSubstitute.Analyzers.Tests.VisualBasic.CodeFixProvidersTests.SyncOverAsyncThrowsCodeFixProviderTests; @@ -42,7 +43,7 @@ End Sub End Class End Namespace"; - await VerifyFix(source, newSource); + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); } public override async Task ReplacesThrowsWithReturns_WhenUsedInProperty(string method, string updatedMethod) @@ -83,7 +84,7 @@ End Sub End Class End Namespace"; - await VerifyFix(source, newSource); + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); } public override async Task ReplacesThrowsWithReturns_WhenUsedInIndexer(string method, string updatedMethod) @@ -124,6 +125,129 @@ End Sub End Class End Namespace"; + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInMethod(string method, string updatedMethod) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface IFoo + Function Bar() As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.Bar().{method}(Of Exception)() + End Sub + End Class +End Namespace"; + + var newSource = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface IFoo + Function Bar() As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.Bar().{updatedMethod}(Of Exception)() + End Sub + End Class +End Namespace"; + + await VerifyFix(source, newSource); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInProperty(string method, string updatedMethod) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface IFoo + Property Bar As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.Bar.{method}(Of Exception)() + End Sub + End Class +End Namespace"; + + var newSource = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface IFoo + Property Bar As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.Bar.{updatedMethod}(Of Exception)() + End Sub + End Class +End Namespace"; + + await VerifyFix(source, newSource); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInIndexer(string method, string updatedMethod) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface IFoo + Default Property Item(ByVal x As Integer) As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute(0).{method}(Of Exception)() + End Sub + End Class +End Namespace"; + + var newSource = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface IFoo + Default Property Item(ByVal x As Integer) As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute(0).{updatedMethod}(Of Exception)() + End Sub + End Class +End Namespace"; + await VerifyFix(source, newSource); } } \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsOrdinaryMethodTests.cs index c7e262cc..9c2897fd 100644 --- a/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsOrdinaryMethodTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsOrdinaryMethodTests.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared; namespace NSubstitute.Analyzers.Tests.VisualBasic.CodeFixProvidersTests.SyncOverAsyncThrowsCodeFixProviderTests; @@ -46,7 +47,7 @@ End Sub End Class End Namespace"; - await VerifyFix(source, newSource); + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); } public override async Task ReplacesThrowsWithReturns_WhenUsedInProperty(string method, string updatedMethod) @@ -91,7 +92,7 @@ End Sub End Class End Namespace"; - await VerifyFix(source, newSource); + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); } public override async Task ReplacesThrowsWithReturns_WhenUsedInIndexer(string method, string updatedMethod) @@ -136,6 +137,231 @@ End Sub End Class End Namespace"; + await VerifyFix(source, newSource, NSubstituteVersion.NSubstitute4_2_2); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInMethod(string method, string updatedMethod) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface IFoo + Function Bar() As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + ExceptionExtensions.{method}(substitute.Bar(), New Exception()) + ExceptionExtensions.{method}(value := substitute.Bar(), ex := New Exception()) + ExceptionExtensions.{method}(ex := New Exception(), value := substitute.Bar()) + ExceptionExtensions.{method}(substitute.Bar(), Function(callInfo) New Exception()) + ExceptionExtensions.{method}(value:= substitute.Bar(), createException:= Function(callInfo) New Exception()) + ExceptionExtensions.{method}(createException:= Function(callInfo) New Exception(), value:= substitute.Bar()) + ExceptionExtensions.{method}(substitute.Bar(), + Function(callInfo) + Return New Exception() + End Function) + ExceptionExtensions.{method}(value:= substitute.Bar(), + createException:= Function(callInfo) + Return New Exception() + End Function) + ExceptionExtensions.{method}( + createException:= Function(callInfo) + Return New Exception() + End Function, value:= substitute.Bar()) + End Sub + End Class +End Namespace"; + + var newSource = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface IFoo + Function Bar() As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + ExceptionExtensions.{updatedMethod}(substitute.Bar(), New Exception()) + ExceptionExtensions.{updatedMethod}(value := substitute.Bar(), ex := New Exception()) + ExceptionExtensions.{updatedMethod}(ex := New Exception(), value := substitute.Bar()) + ExceptionExtensions.{updatedMethod}(substitute.Bar(), Function(callInfo) New Exception()) + ExceptionExtensions.{updatedMethod}(value:= substitute.Bar(), createException:= Function(callInfo) New Exception()) + ExceptionExtensions.{updatedMethod}(createException:= Function(callInfo) New Exception(), value:= substitute.Bar()) + ExceptionExtensions.{updatedMethod}(substitute.Bar(), + Function(callInfo) + Return New Exception() + End Function) + ExceptionExtensions.{updatedMethod}(value:= substitute.Bar(), + createException:= Function(callInfo) + Return New Exception() + End Function) + ExceptionExtensions.{updatedMethod}( + createException:= Function(callInfo) + Return New Exception() + End Function, value:= substitute.Bar()) + End Sub + End Class +End Namespace"; + + await VerifyFix(source, newSource); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInProperty(string method, string updatedMethod) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface IFoo + Property Bar As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + ExceptionExtensions.{method}(substitute.Bar, New Exception()) + ExceptionExtensions.{method}(value := substitute.Bar, ex := New Exception()) + ExceptionExtensions.{method}(ex := New Exception(), value := substitute.Bar) + ExceptionExtensions.{method}(substitute.Bar, Function(callInfo) New Exception()) + ExceptionExtensions.{method}(value:= substitute.Bar, createException:= Function(callInfo) New Exception()) + ExceptionExtensions.{method}(createException:= Function(callInfo) New Exception(), value:= substitute.Bar) + ExceptionExtensions.{method}(substitute.Bar, + Function(callInfo) + Return New Exception() + End Function) + ExceptionExtensions.{method}(value:= substitute.Bar, + createException:= Function(callInfo) + Return New Exception() + End Function) + ExceptionExtensions.{method}( + createException:= Function(callInfo) + Return New Exception() + End Function, value:= substitute.Bar) + End Sub + End Class +End Namespace"; + + var newSource = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface IFoo + Property Bar As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + ExceptionExtensions.{updatedMethod}(substitute.Bar, New Exception()) + ExceptionExtensions.{updatedMethod}(value := substitute.Bar, ex := New Exception()) + ExceptionExtensions.{updatedMethod}(ex := New Exception(), value := substitute.Bar) + ExceptionExtensions.{updatedMethod}(substitute.Bar, Function(callInfo) New Exception()) + ExceptionExtensions.{updatedMethod}(value:= substitute.Bar, createException:= Function(callInfo) New Exception()) + ExceptionExtensions.{updatedMethod}(createException:= Function(callInfo) New Exception(), value:= substitute.Bar) + ExceptionExtensions.{updatedMethod}(substitute.Bar, + Function(callInfo) + Return New Exception() + End Function) + ExceptionExtensions.{updatedMethod}(value:= substitute.Bar, + createException:= Function(callInfo) + Return New Exception() + End Function) + ExceptionExtensions.{updatedMethod}( + createException:= Function(callInfo) + Return New Exception() + End Function, value:= substitute.Bar) + End Sub + End Class +End Namespace"; + + await VerifyFix(source, newSource); + } + + public override async Task ReplacesThrowsWithThrowsAsync_WhenUsedInIndexer(string method, string updatedMethod) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface IFoo + Default Property Item(ByVal x As Integer) As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + ExceptionExtensions.{method}(substitute(0), New Exception()) + ExceptionExtensions.{method}(value := substitute(0), ex := New Exception()) + ExceptionExtensions.{method}(ex := New Exception(), value := substitute(0)) + ExceptionExtensions.{method}(substitute(0), Function(callInfo) New Exception()) + ExceptionExtensions.{method}(value:= substitute(0), createException:= Function(callInfo) New Exception()) + ExceptionExtensions.{method}(createException:= Function(callInfo) New Exception(), value:= substitute(0)) + ExceptionExtensions.{method}(substitute(0), + Function(callInfo) + Return New Exception() + End Function) + ExceptionExtensions.{method}(value:= substitute(0), + createException:= Function(callInfo) + Return New Exception() + End Function) + ExceptionExtensions.{method}( + createException:= Function(callInfo) + Return New Exception() + End Function, value:= substitute(0)) + End Sub + End Class +End Namespace"; + + var newSource = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface IFoo + Default Property Item(ByVal x As Integer) As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + ExceptionExtensions.{updatedMethod}(substitute(0), New Exception()) + ExceptionExtensions.{updatedMethod}(value := substitute(0), ex := New Exception()) + ExceptionExtensions.{updatedMethod}(ex := New Exception(), value := substitute(0)) + ExceptionExtensions.{updatedMethod}(substitute(0), Function(callInfo) New Exception()) + ExceptionExtensions.{updatedMethod}(value:= substitute(0), createException:= Function(callInfo) New Exception()) + ExceptionExtensions.{updatedMethod}(createException:= Function(callInfo) New Exception(), value:= substitute(0)) + ExceptionExtensions.{updatedMethod}(substitute(0), + Function(callInfo) + Return New Exception() + End Function) + ExceptionExtensions.{updatedMethod}(value:= substitute(0), + createException:= Function(callInfo) + Return New Exception() + End Function) + ExceptionExtensions.{updatedMethod}( + createException:= Function(callInfo) + Return New Exception() + End Function, value:= substitute(0)) + End Sub + End Class +End Namespace"; + await VerifyFix(source, newSource); } } \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs index ce21b2e3..d1c75fae 100644 --- a/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/CodeFixProvidersTests/SyncOverAsyncThrowsCodeFixProviderTests/ThrowsAsOrdinaryMethodWithGenericTypeSpecifiedTests.cs @@ -132,4 +132,19 @@ End Class await VerifyFix(source, newSource); } + + public override Task ReplacesThrowsWithThrowsAsync_WhenUsedInMethod(string method, string updatedMethod) + { + throw new System.NotImplementedException(); + } + + public override Task ReplacesThrowsWithThrowsAsync_WhenUsedInProperty(string method, string updatedMethod) + { + throw new System.NotImplementedException(); + } + + public override Task ReplacesThrowsWithThrowsAsync_WhenUsedInIndexer(string method, string updatedMethod) + { + throw new System.NotImplementedException(); + } } \ No newline at end of file