diff --git a/Directory.Build.props b/Directory.Build.props index 8162e6e9..827d02ea 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -8,7 +8,7 @@ - + true diff --git a/NSubstitute.Analyzers.sln b/NSubstitute.Analyzers.sln index 376b393c..e127a92e 100644 --- a/NSubstitute.Analyzers.sln +++ b/NSubstitute.Analyzers.sln @@ -47,6 +47,7 @@ ProjectSection(SolutionItems) = preProject documentation\rules\NS5001.md = documentation\rules\NS5001.md documentation\rules\NS5001.md = documentation\rules\NS5002.md documentation\rules\NS5001.md = documentation\rules\NS5003.md + documentation\rules\NS5001.md = documentation\rules\NS5004.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{1629DF5F-9BC0-49C0-975E-E45C3E58EB3A}" diff --git a/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.CSharp/DiagnosticsSources/WithAnyArgsDiagnosticsSource.cs b/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.CSharp/DiagnosticsSources/WithAnyArgsDiagnosticsSource.cs new file mode 100644 index 00000000..428344ef --- /dev/null +++ b/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.CSharp/DiagnosticsSources/WithAnyArgsDiagnosticsSource.cs @@ -0,0 +1,40 @@ +using NSubstitute.Analyzers.Benchmarks.Source.CSharp.Models; + +namespace NSubstitute.Analyzers.Benchmarks.Source.CSharp.DiagnosticsSources +{ + public class WithAnyArgsDiagnosticsSource + { + public void NS5004_InvalidArgumentMatcherUsedWithAnyArgs() + { + var substitute = Substitute.For(); + + _ = substitute.DidNotReceiveWithAnyArgs()[Arg.Is(1)]; + _ = substitute.DidNotReceiveWithAnyArgs()[Arg.Do(_ => { })]; + substitute.DidNotReceiveWithAnyArgs().IntReturningProperty = Arg.Is(1); + substitute.DidNotReceiveWithAnyArgs().IntReturningProperty = Arg.Do(_ => { }); + substitute.DidNotReceiveWithAnyArgs() + .ObjectReturningMethodWithArguments(Arg.Is(1), Arg.Is(1), Arg.Do(_ => { })); + + _ = substitute.DidNotReceiveWithAnyArgs()[Arg.Is(1)]; + _ = substitute.DidNotReceiveWithAnyArgs()[Arg.Do(_ => { })]; + substitute.DidNotReceiveWithAnyArgs().IntReturningProperty = Arg.Is(1); + substitute.DidNotReceiveWithAnyArgs().IntReturningProperty = Arg.Do(_ => { }); + substitute.DidNotReceiveWithAnyArgs() + .ObjectReturningMethodWithArguments(Arg.Is(1), Arg.Is(1), Arg.Do(_ => { })); + + _ = SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)[Arg.Is(1)]; + _ = SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)[Arg.Do(_ => { })]; + SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute).IntReturningProperty = Arg.Is(1); + SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute).IntReturningProperty = Arg.Do(_ => { }); + SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute) + .ObjectReturningMethodWithArguments(Arg.Is(1), Arg.Is(1), Arg.Do(_ => { })); + + _ = SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)[Arg.Is(1)]; + _ = SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)[Arg.Do(_ => { })]; + SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute).IntReturningProperty = Arg.Is(1); + SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute).IntReturningProperty = Arg.Do(_ => { }); + SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute) + .ObjectReturningMethodWithArguments(Arg.Is(1), Arg.Is(1), Arg.Do(_ => { })); + } + } +} diff --git a/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.CSharp/Models/Foo.cs b/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.CSharp/Models/Foo.cs index 06c97121..4c08092d 100644 --- a/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.CSharp/Models/Foo.cs +++ b/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.CSharp/Models/Foo.cs @@ -42,7 +42,7 @@ public Task GenericTaskReturningAsyncMethod() public ConfiguredCall ConfiguredCallReturningProperty { get; } - public int IntReturningProperty { get; } + public int IntReturningProperty { get; set; } public IFoo ObjectReturningProperty { get; } diff --git a/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.CSharp/Models/IFoo.cs b/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.CSharp/Models/IFoo.cs index 436fb090..3dc8aff5 100644 --- a/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.CSharp/Models/IFoo.cs +++ b/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.CSharp/Models/IFoo.cs @@ -18,7 +18,7 @@ public interface IFoo ConfiguredCall ConfiguredCallReturningProperty { get; } - int IntReturningProperty { get; } + int IntReturningProperty { get; set; } IFoo ObjectReturningProperty { get; } diff --git a/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.VisualBasic/DiagnosticsSources/WithAnyArgsDiagnosticsSource.vb b/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.VisualBasic/DiagnosticsSources/WithAnyArgsDiagnosticsSource.vb new file mode 100644 index 00000000..263a5383 --- /dev/null +++ b/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.VisualBasic/DiagnosticsSources/WithAnyArgsDiagnosticsSource.vb @@ -0,0 +1,54 @@ +Imports System +Imports NSubstitute.Analyzers.Benchmarks.Source.VisualBasic.Models + +Namespace DiagnosticsSources + Public Class WithAnyArgsDiagnosticsSource + Public Sub NS5004_InvalidArgumentMatcherUsedWithAnyArgs() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + Dim fist = substitute.DidNotReceiveWithAnyArgs()(Arg.[Is](1)) + Dim second = substitute.DidNotReceiveWithAnyArgs()(Arg.[Do](Of Integer)(Function(x) + Throw New Exception + End Function)) + substitute.DidNotReceiveWithAnyArgs().IntReturningProperty = Arg.[Is](1) + substitute.DidNotReceiveWithAnyArgs().IntReturningProperty = Arg.[Do](Of Integer)(Function(x) + Throw New Exception + End Function) + substitute.DidNotReceiveWithAnyArgs().ObjectReturningMethodWithArguments(Arg.[Is](1), Arg.[Is](1), Arg.[Do](Of Integer)(Function(x) + Throw New Exception + End Function)) + Dim third = substitute.DidNotReceiveWithAnyArgs()(Arg.[Is](1)) + Dim fourth = substitute.DidNotReceiveWithAnyArgs()(Arg.[Do](Of Integer)(Function(x) + Throw New Exception() + End Function)) + substitute.DidNotReceiveWithAnyArgs().IntReturningProperty = Arg.[Is](1) + substitute.DidNotReceiveWithAnyArgs().IntReturningProperty = Arg.[Do](Of Integer)(Function(x) + Throw New Exception() + End Function) + substitute.DidNotReceiveWithAnyArgs().ObjectReturningMethodWithArguments(Arg.[Is](1), Arg.[Is](1), Arg.[Do](Of Integer)(Function(x) + Throw New Exception() + End Function)) + Dim fifth = SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)(Arg.[Is](1)) + Dim sixth = SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)(Arg.[Do](Of Integer)(Function(x) + Throw New Exception() + End Function)) + SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute).IntReturningProperty = Arg.[Is](1) + SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute).IntReturningProperty = Arg.[Do](Of Integer)(Function(x) + Throw New Exception() + End Function) + SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute).ObjectReturningMethodWithArguments(Arg.[Is](1), Arg.[Is](1), Arg.[Do](Of Integer)(Function(x) + Throw New Exception() + End Function)) + Dim seventh = SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)(Arg.[Is](1)) + Dim eigth = SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)(Arg.[Do](Of Integer)(Function(x) + Throw New Exception() + End Function)) + SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute).IntReturningProperty = Arg.[Is](1) + SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute).IntReturningProperty = Arg.[Do](Of Integer)(Function(x) + Throw New Exception() + End Function) + SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute).ObjectReturningMethodWithArguments(Arg.[Is](1), Arg.[Is](1), Arg.[Do](Of Integer)(Function(x) + Throw New Exception() + End Function)) + End Sub + End Class +End Namespace diff --git a/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.VisualBasic/Models/Foo.vb b/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.VisualBasic/Models/Foo.vb index 36624676..519b30f4 100644 --- a/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.VisualBasic/Models/Foo.vb +++ b/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.VisualBasic/Models/Foo.vb @@ -62,7 +62,7 @@ Namespace Models End Get End Property - Public ReadOnly Property IntReturningProperty As Integer Implements IFoo.IntReturningProperty + Public Property IntReturningProperty As Integer Implements IFoo.IntReturningProperty Public ReadOnly Property ObjectReturningProperty As IFoo Implements IFoo.ObjectReturningProperty Default Public ReadOnly Property Item(a As Integer) As Integer Implements IFoo.Item diff --git a/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.VisualBasic/Models/IFoo.vb b/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.VisualBasic/Models/IFoo.vb index d24aba2b..45857605 100644 --- a/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.VisualBasic/Models/IFoo.vb +++ b/benchmarks/NSubstitute.Analyzers.Benchmarks.Source.VisualBasic/Models/IFoo.vb @@ -16,7 +16,7 @@ Namespace Models ReadOnly Property ConfiguredCallReturningProperty As ConfiguredCall - ReadOnly Property IntReturningProperty As Integer + Property IntReturningProperty As Integer ReadOnly Property ObjectReturningProperty As IFoo diff --git a/benchmarks/NSubstitute.Analyzers.Benchmarks/CSharp/CSharpDiagnosticAnalyzersBenchmarks.cs b/benchmarks/NSubstitute.Analyzers.Benchmarks/CSharp/CSharpDiagnosticAnalyzersBenchmarks.cs index a5988118..252fe780 100644 --- a/benchmarks/NSubstitute.Analyzers.Benchmarks/CSharp/CSharpDiagnosticAnalyzersBenchmarks.cs +++ b/benchmarks/NSubstitute.Analyzers.Benchmarks/CSharp/CSharpDiagnosticAnalyzersBenchmarks.cs @@ -33,6 +33,8 @@ public class CSharpDiagnosticAnalyzersBenchmarks : AbstractDiagnosticAnalyzersBe protected override AnalyzerBenchmark SyncOverAsyncThrowsAnalyzerBenchmark { get; } + protected override AnalyzerBenchmark WithAnyArgsAnalyzerBenchmark { get; } + protected override AbstractSolutionLoader SolutionLoader { get; } protected override string SourceProjectFolderName { get; } @@ -58,5 +60,6 @@ public CSharpDiagnosticAnalyzersBenchmarks() ReceivedInReceivedInOrderAnalyzerBenchmark = AnalyzerBenchmark.CreateBenchmark(Solution, new ReceivedInReceivedInOrderAnalyzer()); AsyncReceivedInOrderCallbackAnalyzerBenchmark = AnalyzerBenchmark.CreateBenchmark(Solution, new AsyncReceivedInOrderCallbackAnalyzer()); SyncOverAsyncThrowsAnalyzerBenchmark = AnalyzerBenchmark.CreateBenchmark(Solution, new SyncOverAsyncThrowsAnalyzer()); - } + WithAnyArgsAnalyzerBenchmark = AnalyzerBenchmark.CreateBenchmark(Solution, new WithAnyArgsArgumentMatcherAnalyzer()); + } } \ No newline at end of file diff --git a/benchmarks/NSubstitute.Analyzers.Benchmarks/Shared/AbstractDiagnosticAnalyzersBenchmarks.cs b/benchmarks/NSubstitute.Analyzers.Benchmarks/Shared/AbstractDiagnosticAnalyzersBenchmarks.cs index 2f463fdd..ce156a07 100644 --- a/benchmarks/NSubstitute.Analyzers.Benchmarks/Shared/AbstractDiagnosticAnalyzersBenchmarks.cs +++ b/benchmarks/NSubstitute.Analyzers.Benchmarks/Shared/AbstractDiagnosticAnalyzersBenchmarks.cs @@ -40,6 +40,8 @@ public abstract class AbstractDiagnosticAnalyzersBenchmarks protected abstract AnalyzerBenchmark SyncOverAsyncThrowsAnalyzerBenchmark { get; } + protected abstract AnalyzerBenchmark WithAnyArgsAnalyzerBenchmark { get; } + protected abstract AbstractSolutionLoader SolutionLoader { get; } protected abstract string SourceProjectFolderName { get; } diff --git a/benchmarks/NSubstitute.Analyzers.Benchmarks/VisualBasic/VisualBasicDiagnosticAnalyzersBenchmarks.cs b/benchmarks/NSubstitute.Analyzers.Benchmarks/VisualBasic/VisualBasicDiagnosticAnalyzersBenchmarks.cs index 1e9055b7..0d1a495d 100644 --- a/benchmarks/NSubstitute.Analyzers.Benchmarks/VisualBasic/VisualBasicDiagnosticAnalyzersBenchmarks.cs +++ b/benchmarks/NSubstitute.Analyzers.Benchmarks/VisualBasic/VisualBasicDiagnosticAnalyzersBenchmarks.cs @@ -33,6 +33,8 @@ public class VisualBasicDiagnosticAnalyzersBenchmarks : AbstractDiagnosticAnalyz protected override AnalyzerBenchmark SyncOverAsyncThrowsAnalyzerBenchmark { get; } + protected override AnalyzerBenchmark WithAnyArgsAnalyzerBenchmark { get; } + protected override AbstractSolutionLoader SolutionLoader { get; } protected override string SourceProjectFolderName { get; } @@ -58,5 +60,6 @@ public VisualBasicDiagnosticAnalyzersBenchmarks() ReceivedInReceivedInOrderAnalyzerBenchmark = AnalyzerBenchmark.CreateBenchmark(Solution, new ReceivedInReceivedInOrderAnalyzer()); AsyncReceivedInOrderCallbackAnalyzerBenchmark = AnalyzerBenchmark.CreateBenchmark(Solution, new AsyncReceivedInOrderCallbackAnalyzer()); SyncOverAsyncThrowsAnalyzerBenchmark = AnalyzerBenchmark.CreateBenchmark(Solution, new SyncOverAsyncThrowsAnalyzer()); + WithAnyArgsAnalyzerBenchmark = AnalyzerBenchmark.CreateBenchmark(Solution, new WithAnyArgsArgumentMatcherAnalyzer()); } } \ No newline at end of file diff --git a/documentation/rules/NS5004.md b/documentation/rules/NS5004.md new file mode 100644 index 00000000..a7087c77 --- /dev/null +++ b/documentation/rules/NS5004.md @@ -0,0 +1,52 @@ +# NS5004 + + + + + + + + + + +
CheckIdNS5004
CategoryUsage
+ +## Cause + +Argument matcher used with WithAnyArgs. This matcher may not be executed. + +## Rule description + +A violation of this rule occurs when `ReturnsForAnyArgs`,`ReturnsNullForAnyArgs`,`ThrowsForAnyArgs`,`ThrowsAsyncForAnyArgs`, `WhenForAnyArgs`, `DidNotReceiveWithAnyArgs` or `ReceivedWithAnyArgs` is used in combination with arg matchers other than `Arg.Any`. + +## How to fix violations + +To fix a violation of this rule, replace arg matchers used in aforementioned methods with `Arg.Any` + +For example: + +````c# +// Incorrect: +sub.DidNotReceiveWithAnyArgs().Bar(Arg.Is(1)); + +// Correct: +sub.DidNotReceiveWithAnyArgs().Bar(Arg.Any()); + +// The following are also correct. The exact arguments will not be checked due to *WithAnyArgs. +sub.DidNotReceiveWithAnyArgs().Bar(default(int)); +sub.DidNotReceiveWithAnyArgs().Bar(123); +```` + +## How to suppress violations + +This warning can be suppressed by disabling the warning in the **ruleset** file for the project. +The warning can also be suppressed programmatically for an assembly: +````c# +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "NS5004:Argument matcher used with WithAnyArgs. This matcher may not be executed..", Justification = "Reviewed")] +```` + +Or for a specific code block: +````c# +#pragma warning disable NS5004 // Argument matcher used with WithAnyArgs. This matcher may not be executed.. +// the code which produces warning +#pragma warning restore NS5004 // Argument matcher used with WithAnyArgs. This matcher may not be executed.. diff --git a/documentation/rules/README.md b/documentation/rules/README.md index ad3710bd..fdce0a7d 100644 --- a/documentation/rules/README.md +++ b/documentation/rules/README.md @@ -29,3 +29,4 @@ | [NS5001](NS5001.md) | Usage | Usage of received-like method in `Received.InOrder` callback. | | [NS5002](NS5002.md) | Usage | Usage of async callback in `Received.InOrder` method. | | [NS5003](NS5003.md) | Usage | Sync `Throws` used in async method. | +| [NS5004](NS5004.md) | Usage | Argument matcher used with WithAnyArgs method. | diff --git a/src/NSubstitute.Analyzers.CSharp/DiagnosticAnalyzers/SubstitutionNodeFinder.cs b/src/NSubstitute.Analyzers.CSharp/DiagnosticAnalyzers/SubstitutionNodeFinder.cs index 9abf19ea..6d8e8279 100644 --- a/src/NSubstitute.Analyzers.CSharp/DiagnosticAnalyzers/SubstitutionNodeFinder.cs +++ b/src/NSubstitute.Analyzers.CSharp/DiagnosticAnalyzers/SubstitutionNodeFinder.cs @@ -25,6 +25,7 @@ protected override SyntaxNode GetSubstitutionActualNode(SyntaxNodeAnalysisContex return syntaxNode.GetSubstitutionActualNode(node => syntaxNodeContext.SemanticModel.GetSymbolInfo(node).Symbol); } + // TODO replace with SyntaxVisitor or OperationVisitor protected override IEnumerable FindInvocations(SyntaxNodeAnalysisContext syntaxNodeContext, SyntaxNode argumentSyntax) { SyntaxNode body = null; diff --git a/src/NSubstitute.Analyzers.CSharp/DiagnosticAnalyzers/WithAnyArgsArgumentMatcherAnalyzer.cs b/src/NSubstitute.Analyzers.CSharp/DiagnosticAnalyzers/WithAnyArgsArgumentMatcherAnalyzer.cs new file mode 100644 index 00000000..fb347b87 --- /dev/null +++ b/src/NSubstitute.Analyzers.CSharp/DiagnosticAnalyzers/WithAnyArgsArgumentMatcherAnalyzer.cs @@ -0,0 +1,26 @@ +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using NSubstitute.Analyzers.Shared.DiagnosticAnalyzers; + +namespace NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +internal sealed class WithAnyArgsArgumentMatcherAnalyzer : AbstractWithAnyArgsArgumentMatcherAnalyzer +{ + internal static ImmutableHashSet MaybeAllowedAncestors { get; } = ImmutableHashSet.Create( + (int)SyntaxKind.InvocationExpression, + (int)SyntaxKind.ElementAccessExpression, + (int)SyntaxKind.SimpleAssignmentExpression); + + public WithAnyArgsArgumentMatcherAnalyzer() + : base(CSharp.DiagnosticDescriptorsProvider.Instance, SubstitutionNodeFinder.Instance) + { + } + + protected override ImmutableHashSet MaybeAllowedArgMatcherAncestors { get; } = MaybeAllowedAncestors; + + protected override SyntaxKind InvocationExpressionKind { get; } = SyntaxKind.InvocationExpression; +} \ No newline at end of file diff --git a/src/NSubstitute.Analyzers.Shared/AbstractDiagnosticDescriptorsProvider.cs b/src/NSubstitute.Analyzers.Shared/AbstractDiagnosticDescriptorsProvider.cs index ce607118..f63dbb34 100644 --- a/src/NSubstitute.Analyzers.Shared/AbstractDiagnosticDescriptorsProvider.cs +++ b/src/NSubstitute.Analyzers.Shared/AbstractDiagnosticDescriptorsProvider.cs @@ -52,6 +52,8 @@ internal class AbstractDiagnosticDescriptorsProvider : IDiagnosticDescriptors public DiagnosticDescriptor NonSubstitutableMemberArgumentMatcherUsage { get; } = DiagnosticDescriptors.NonSubstitutableMemberArgumentMatcherUsage; + public DiagnosticDescriptor WithAnyArgsArgumentMatcherUsage { get; } = DiagnosticDescriptors.WithAnyArgsArgumentMatcherUsage; + public DiagnosticDescriptor ReceivedUsedInReceivedInOrder { get; } = DiagnosticDescriptors.ReceivedUsedInReceivedInOrder; public DiagnosticDescriptor AsyncCallbackUsedInReceivedInOrder { get; } = DiagnosticDescriptors.AsyncCallbackUsedInReceivedInOrder; diff --git a/src/NSubstitute.Analyzers.Shared/CodeFixProviders/AbstractSyncOverAsyncThrowsCodeFixProvider.cs b/src/NSubstitute.Analyzers.Shared/CodeFixProviders/AbstractSyncOverAsyncThrowsCodeFixProvider.cs index 976db029..0706f2ed 100644 --- a/src/NSubstitute.Analyzers.Shared/CodeFixProviders/AbstractSyncOverAsyncThrowsCodeFixProvider.cs +++ b/src/NSubstitute.Analyzers.Shared/CodeFixProviders/AbstractSyncOverAsyncThrowsCodeFixProvider.cs @@ -93,7 +93,7 @@ private async Task CreateThrowsAsyncInvocationExpression( CodeFixContext context) { var updatedMethodName = - invocationSymbol.IsThrowsSyncForAnyArgsMethod() + invocationSymbol.IsThrowsSyncMethod() ? MetadataNames.NSubstituteThrowsAsyncMethod : MetadataNames.NSubstituteThrowsAsyncForAnyArgsMethod; @@ -120,7 +120,7 @@ private async Task CreateReturnInvocationExpression( CreateFromExceptionInvocationExpression(syntaxGenerator, invocationOperation); var returnsMethodName = - invocationSymbol.IsThrowsSyncForAnyArgsMethod() ? "Returns" : "ReturnsForAnyArgs"; + invocationSymbol.IsThrowsSyncMethod() ? "Returns" : "ReturnsForAnyArgs"; if (invocationSymbol.MethodKind == MethodKind.Ordinary) { @@ -213,12 +213,12 @@ private static string GetReplacementMethodName(IMethodSymbol methodSymbol, bool { if (useModernSyntax) { - return methodSymbol.IsThrowsSyncForAnyArgsMethod() + return methodSymbol.IsThrowsSyncMethod() ? MetadataNames.NSubstituteThrowsAsyncMethod : MetadataNames.NSubstituteThrowsAsyncForAnyArgsMethod; } - return methodSymbol.IsThrowsSyncForAnyArgsMethod() + return methodSymbol.IsThrowsSyncMethod() ? MetadataNames.NSubstituteReturnsMethod : MetadataNames.NSubstituteReturnsForAnyArgsMethod; } diff --git a/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractWithAnyArgsArgumentMatcherAnalyzer.cs b/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractWithAnyArgsArgumentMatcherAnalyzer.cs new file mode 100644 index 00000000..9696ac6b --- /dev/null +++ b/src/NSubstitute.Analyzers.Shared/DiagnosticAnalyzers/AbstractWithAnyArgsArgumentMatcherAnalyzer.cs @@ -0,0 +1,231 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; +using NSubstitute.Analyzers.Shared.Extensions; + +namespace NSubstitute.Analyzers.Shared.DiagnosticAnalyzers; + +internal abstract class AbstractWithAnyArgsArgumentMatcherAnalyzer : AbstractDiagnosticAnalyzer + where TInvocationExpressionSyntax : SyntaxNode + where TSyntaxKind : struct, Enum +{ + private readonly ISubstitutionNodeFinder _substitutionNodeFinder; + private readonly Action _analyzeInvocationAction; + + protected abstract ImmutableHashSet MaybeAllowedArgMatcherAncestors { get; } + + protected abstract TSyntaxKind InvocationExpressionKind { get; } + + protected AbstractWithAnyArgsArgumentMatcherAnalyzer( + IDiagnosticDescriptorsProvider diagnosticDescriptorsProvider, + ISubstitutionNodeFinder substitutionNodeFinder) + : base(diagnosticDescriptorsProvider) + { + _substitutionNodeFinder = substitutionNodeFinder; + _analyzeInvocationAction = AnalyzeInvocation; + SupportedDiagnostics = ImmutableArray.Create(DiagnosticDescriptorsProvider.WithAnyArgsArgumentMatcherUsage); + } + + public override ImmutableArray SupportedDiagnostics { get; } + + protected override void InitializeAnalyzer(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(_analyzeInvocationAction, InvocationExpressionKind); + } + + private void AnalyzeInvocation(SyntaxNodeAnalysisContext syntaxNodeContext) + { + var invocationExpression = (TInvocationExpressionSyntax)syntaxNodeContext.Node; + var methodSymbolInfo = syntaxNodeContext.SemanticModel.GetSymbolInfo(invocationExpression); + + if (methodSymbolInfo.Symbol is not IMethodSymbol methodSymbol) + { + return; + } + + if (methodSymbol.IsWithAnyArgsIncompatibleArgMatcherLikeMethod()) + { + AnalyzeArgLikeMethodForReceivedWithAnyArgs(syntaxNodeContext, invocationExpression, methodSymbol); + return; + } + + if (methodSymbol.IsReturnForAnyArgsLikeMethod() || methodSymbol.IsThrowForAnyArgsLikeMethod()) + { + AnalyzeReturnsLikeMethod(syntaxNodeContext, invocationExpression); + return; + } + + if (methodSymbol.IsWhenForAnyArgsLikeMethod()) + { + AnalyzeWhenLikeMethod(syntaxNodeContext, invocationExpression); + } + } + + private void AnalyzeWhenLikeMethod( + SyntaxNodeAnalysisContext syntaxNodeContext, + TInvocationExpressionSyntax whenMethod) + { + ImmutableArray Arguments(IPropertyReferenceOperation propertyReferenceOperation) + { + var builder = ImmutableArray.CreateBuilder(propertyReferenceOperation.Arguments.Length); + builder.AddRange(propertyReferenceOperation.Arguments); + + if (propertyReferenceOperation.Parent is IAssignmentOperation assignmentOperation) + { + builder.Add(assignmentOperation.Value); + } + + return builder.ToImmutable(); + } + + if (syntaxNodeContext.SemanticModel.GetOperation(whenMethod) is not IInvocationOperation invocationOperation) + { + return; + } + + foreach (var syntaxNode in _substitutionNodeFinder.FindForWhenExpression(syntaxNodeContext, invocationOperation)) + { + var substitutedOperation = syntaxNodeContext.SemanticModel.GetOperation(syntaxNode); + + IReadOnlyList arguments = substitutedOperation switch + { + IInvocationOperation substituteInvocationOperation => substituteInvocationOperation.Arguments, + IPropertyReferenceOperation propertyReferenceOperation => Arguments(propertyReferenceOperation), + _ => ImmutableArray.Empty + }; + + foreach (var operation in arguments) + { + AnalyzeArgument(syntaxNodeContext, operation); + } + } + } + + private void AnalyzeReturnsLikeMethod( + SyntaxNodeAnalysisContext syntaxNodeContext, + TInvocationExpressionSyntax returnsInvocationExpression) + { + if (syntaxNodeContext.SemanticModel.GetOperation(returnsInvocationExpression) is not IInvocationOperation + invocationOperation) + { + return; + } + + var substitutedOperation = + _substitutionNodeFinder.FindOperationForStandardExpression(invocationOperation); + + var arguments = GetArguments(substitutedOperation); + + foreach (var argumentOperation in arguments) + { + AnalyzeArgument(syntaxNodeContext, argumentOperation); + } + } + + private static IReadOnlyList GetArguments(IOperation substitutedOperation) + { + IReadOnlyList arguments = substitutedOperation switch + { + IInvocationOperation substituteInvocationOperation => substituteInvocationOperation.Arguments, + IPropertyReferenceOperation propertyReferenceOperation => propertyReferenceOperation.Arguments, + IConversionOperation conversionOperation => GetArguments(conversionOperation.Operand), + _ => ImmutableArray.Empty + }; + return arguments; + } + + private void AnalyzeArgument(SyntaxNodeAnalysisContext syntaxNodeContext, IOperation operation) + { + if (operation is IConversionOperation conversionOperation) + { + AnalyzeArgument(syntaxNodeContext, conversionOperation.Operand); + return; + } + + if (operation is IArgumentOperation argumentOperation) + { + AnalyzeArgument(syntaxNodeContext, argumentOperation.Value); + return; + } + + if (operation is IInvocationOperation argInvocationOperation && + argInvocationOperation.TargetMethod.IsWithAnyArgsIncompatibleArgMatcherLikeMethod()) + { + syntaxNodeContext.TryReportDiagnostic( + Diagnostic.Create( + DiagnosticDescriptorsProvider.WithAnyArgsArgumentMatcherUsage, + argInvocationOperation.Syntax.GetLocation()), + argInvocationOperation.TargetMethod); + } + } + + private void AnalyzeArgLikeMethodForReceivedWithAnyArgs( + SyntaxNodeAnalysisContext syntaxNodeContext, + TInvocationExpressionSyntax argInvocationExpression, + IMethodSymbol invocationExpressionSymbol) + { + var enclosingExpression = FindMaybeAllowedEnclosingExpression(argInvocationExpression); + + if (enclosingExpression == null) + { + return; + } + + var operation = syntaxNodeContext.SemanticModel.GetOperation(enclosingExpression); + + if (operation is IInvocationOperation enclosingInvocationOperation && + enclosingInvocationOperation.Instance is IInvocationOperation enclosingInvocationOperationInstance && + enclosingInvocationOperationInstance.TargetMethod.IsReceivedWithAnyArgsLikeMethod()) + { + syntaxNodeContext.TryReportDiagnostic( + Diagnostic.Create( + DiagnosticDescriptorsProvider.WithAnyArgsArgumentMatcherUsage, + argInvocationExpression.GetLocation()), + invocationExpressionSymbol); + return; + } + + var memberReferenceOperation = GetMemberReferenceOperation(operation); + + if (memberReferenceOperation is not { Instance: IInvocationOperation invocationOperation } || + !invocationOperation.TargetMethod.IsReceivedWithAnyArgsLikeMethod()) + { + return; + } + + syntaxNodeContext.TryReportDiagnostic( + Diagnostic.Create( + DiagnosticDescriptorsProvider.WithAnyArgsArgumentMatcherUsage, + argInvocationExpression.GetLocation()), + invocationExpressionSymbol); + } + + private static IMemberReferenceOperation GetMemberReferenceOperation(IOperation operation) + { + return operation switch + { + IAssignmentOperation { Target: IMemberReferenceOperation memberReferenceOperation } => + memberReferenceOperation, + IBinaryOperation { LeftOperand: IMemberReferenceOperation binaryMemberReferenceOperation } => + binaryMemberReferenceOperation, + IBinaryOperation { LeftOperand: IConversionOperation { Operand: IMemberReferenceOperation conversionMemberReference } } => + conversionMemberReference, + IExpressionStatementOperation { Operation: ISimpleAssignmentOperation { Target: IMemberReferenceOperation memberReferenceOperation } } => + memberReferenceOperation, + _ => null + }; + } + + private SyntaxNode FindMaybeAllowedEnclosingExpression(TInvocationExpressionSyntax invocationExpression) => + FindEnclosingExpression(invocationExpression, MaybeAllowedArgMatcherAncestors); + + private static SyntaxNode FindEnclosingExpression(TInvocationExpressionSyntax invocationExpression, ImmutableHashSet ancestors) + { + return invocationExpression.Ancestors() + .FirstOrDefault(ancestor => ancestors.Contains(ancestor.RawKind)); + } +} \ No newline at end of file diff --git a/src/NSubstitute.Analyzers.Shared/DiagnosticCategories.cs b/src/NSubstitute.Analyzers.Shared/DiagnosticCategory.cs similarity index 100% rename from src/NSubstitute.Analyzers.Shared/DiagnosticCategories.cs rename to src/NSubstitute.Analyzers.Shared/DiagnosticCategory.cs diff --git a/src/NSubstitute.Analyzers.Shared/DiagnosticDescriptors.cs b/src/NSubstitute.Analyzers.Shared/DiagnosticDescriptors.cs index 573cb8aa..b05ae0bb 100644 --- a/src/NSubstitute.Analyzers.Shared/DiagnosticDescriptors.cs +++ b/src/NSubstitute.Analyzers.Shared/DiagnosticDescriptors.cs @@ -205,6 +205,14 @@ internal class DiagnosticDescriptors defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true); + public static DiagnosticDescriptor WithAnyArgsArgumentMatcherUsage { get; } = + CreateDiagnosticDescriptor( + name: nameof(WithAnyArgsArgumentMatcherUsage), + id: DiagnosticIdentifiers.WithAnyArgsArgumentMatcherUsage, + category: DiagnosticCategory.Usage.GetDisplayName(), + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + public static DiagnosticDescriptor ReceivedUsedInReceivedInOrder { get; } = CreateDiagnosticDescriptor( name: nameof(ReceivedUsedInReceivedInOrder), id: DiagnosticIdentifiers.ReceivedUsedInReceivedInOrder, diff --git a/src/NSubstitute.Analyzers.Shared/DiagnosticIdentifiers.cs b/src/NSubstitute.Analyzers.Shared/DiagnosticIdentifiers.cs index c704dce0..28d5a613 100644 --- a/src/NSubstitute.Analyzers.Shared/DiagnosticIdentifiers.cs +++ b/src/NSubstitute.Analyzers.Shared/DiagnosticIdentifiers.cs @@ -32,4 +32,5 @@ internal class DiagnosticIdentifiers public const string ReceivedUsedInReceivedInOrder = "NS5001"; public const string AsyncCallbackUsedInReceivedInOrder = "NS5002"; public const string SyncOverAsyncThrows = "NS5003"; + public const string WithAnyArgsArgumentMatcherUsage = "NS5004"; } \ 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 7bbfd1f5..30c244a8 100644 --- a/src/NSubstitute.Analyzers.Shared/Extensions/SubstituteSymbolExtensions.cs +++ b/src/NSubstitute.Analyzers.Shared/Extensions/SubstituteSymbolExtensions.cs @@ -37,17 +37,27 @@ public static bool IsReturnLikeMethod(this ISymbol symbol) return IsMember(symbol, MetadataNames.ReturnsMethodNames); } + public static bool IsReturnForAnyArgsLikeMethod(this ISymbol symbol) + { + return IsMember(symbol, MetadataNames.ReturnsForAnyArgsMethodNames); + } + public static bool IsThrowLikeMethod(this ISymbol symbol) { return IsMember(symbol, MetadataNames.ThrowsMethodNames); } + public static bool IsThrowForAnyArgsLikeMethod(this ISymbol symbol) + { + return IsMember(symbol, MetadataNames.ThrowsForAnyArgsMethodNames); + } + public static bool IsThrowSyncLikeMethod(this ISymbol symbol) { return IsMember(symbol, MetadataNames.ThrowsSyncMethodNames); } - public static bool IsThrowsSyncForAnyArgsMethod(this ISymbol symbol) + public static bool IsThrowsSyncMethod(this ISymbol symbol) { return IsMember( symbol, @@ -61,6 +71,11 @@ public static bool IsReceivedLikeMethod(this ISymbol symbol) IsMember(symbol, MetadataNames.ReceivedWithQuantityMethodNames); } + public static bool IsReceivedWithAnyArgsLikeMethod(this ISymbol symbol) + { + return IsMember(symbol, MetadataNames.ReceivedWithAnyArgsMethodNames) || IsMember(symbol, MetadataNames.ReceivedWithAnyArgsQuantityMethodNames); + } + public static bool IsReceivedInOrderMethod(this ISymbol symbol) { return IsMember(symbol, MetadataNames.NSubstituteInOrderMethod, MetadataNames.NSubstituteReceivedFullTypeName); @@ -71,11 +86,22 @@ public static bool IsWhenLikeMethod(this ISymbol symbol) return IsMember(symbol, MetadataNames.WhenMethodNames); } + public static bool IsWhenForAnyArgsLikeMethod(this ISymbol symbol) + { + return IsMember(symbol, MetadataNames.WhenForAnyArgsMethodNames); + } + public static bool IsArgMatcherLikeMethod(this ISymbol symbol) { return IsMember(symbol, MetadataNames.ArgMatchersMethodNames) || IsMember(symbol, MetadataNames.ArgMatchersCompatMethodNames); } + public static bool IsWithAnyArgsIncompatibleArgMatcherLikeMethod(this ISymbol symbol) + { + return IsMember(symbol, MetadataNames.ArgMatchersIncompatibleWithForAnyArgsMethodNames) || + IsMember(symbol, MetadataNames.ArgMatchersCompatIncompatibleWithForAnyArgsMethodNames); + } + public static bool IsArgDoLikeMethod(this ISymbol symbol) { return IsMember(symbol, MetadataNames.ArgDoMethodName, MetadataNames.NSubstituteArgFullTypeName) || diff --git a/src/NSubstitute.Analyzers.Shared/IDiagnosticDescriptorsProvider.cs b/src/NSubstitute.Analyzers.Shared/IDiagnosticDescriptorsProvider.cs index 8521ab63..2f326e70 100644 --- a/src/NSubstitute.Analyzers.Shared/IDiagnosticDescriptorsProvider.cs +++ b/src/NSubstitute.Analyzers.Shared/IDiagnosticDescriptorsProvider.cs @@ -52,6 +52,8 @@ internal interface IDiagnosticDescriptorsProvider DiagnosticDescriptor NonSubstitutableMemberArgumentMatcherUsage { get; } + DiagnosticDescriptor WithAnyArgsArgumentMatcherUsage { get; } + DiagnosticDescriptor ReceivedUsedInReceivedInOrder { get; } DiagnosticDescriptor AsyncCallbackUsedInReceivedInOrder { get; } diff --git a/src/NSubstitute.Analyzers.Shared/MetadataNames.cs b/src/NSubstitute.Analyzers.Shared/MetadataNames.cs index ea8d10a4..87e15649 100644 --- a/src/NSubstitute.Analyzers.Shared/MetadataNames.cs +++ b/src/NSubstitute.Analyzers.Shared/MetadataNames.cs @@ -57,6 +57,12 @@ internal class MetadataNames [NSubstituteReturnsNullForAnyArgsMethod] = NSubstituteReturnsExtensionsFullTypeName }; + public static readonly IReadOnlyDictionary ReturnsForAnyArgsMethodNames = new Dictionary + { + [NSubstituteReturnsForAnyArgsMethod] = NSubstituteSubstituteExtensionsFullTypeName, + [NSubstituteReturnsNullForAnyArgsMethod] = NSubstituteReturnsExtensionsFullTypeName + }; + public static readonly IReadOnlyDictionary ThrowsMethodNames = new Dictionary { [NSubstituteThrowsMethod] = NSubstituteExceptionExtensionsFullTypeName, @@ -65,6 +71,12 @@ internal class MetadataNames [NSubstituteThrowsAsyncForAnyArgsMethod] = NSubstituteExceptionExtensionsFullTypeName }; + public static readonly IReadOnlyDictionary ThrowsForAnyArgsMethodNames = new Dictionary + { + [NSubstituteThrowsForAnyArgsMethod] = NSubstituteExceptionExtensionsFullTypeName, + [NSubstituteThrowsAsyncForAnyArgsMethod] = NSubstituteExceptionExtensionsFullTypeName + }; + public static readonly IReadOnlyDictionary ThrowsSyncMethodNames = new Dictionary { [NSubstituteThrowsMethod] = NSubstituteExceptionExtensionsFullTypeName, @@ -85,12 +97,29 @@ internal class MetadataNames [NSubstituteReceivedWithAnyArgsMethod] = NSubstituteReceivedExtensionsFullTypeName, }; + public static readonly IReadOnlyDictionary ReceivedWithAnyArgsMethodNames = new Dictionary + { + [NSubstituteReceivedWithAnyArgsMethod] = NSubstituteSubstituteExtensionsFullTypeName, + [NSubstituteDidNotReceiveWithAnyArgsMethod] = NSubstituteSubstituteExtensionsFullTypeName + }; + + public static readonly IReadOnlyDictionary ReceivedWithAnyArgsQuantityMethodNames = new Dictionary + { + [NSubstituteReceivedWithAnyArgsMethod] = NSubstituteReceivedExtensionsFullTypeName, + [NSubstituteDidNotReceiveWithAnyArgsMethod] = NSubstituteReceivedExtensionsFullTypeName + }; + public static readonly IReadOnlyDictionary WhenMethodNames = new Dictionary { [NSubstituteWhenMethod] = NSubstituteSubstituteExtensionsFullTypeName, [NSubstituteWhenForAnyArgsMethod] = NSubstituteSubstituteExtensionsFullTypeName }; + public static readonly IReadOnlyDictionary WhenForAnyArgsMethodNames = new Dictionary + { + [NSubstituteWhenForAnyArgsMethod] = NSubstituteSubstituteExtensionsFullTypeName + }; + public static readonly IReadOnlyDictionary ArgMatchersMethodNames = new Dictionary { [ArgIsMethodName] = NSubstituteArgFullTypeName, @@ -109,6 +138,22 @@ internal class MetadataNames [ArgInvokeDelegateMethodName] = NSubstituteArgCompatFullTypeName }; + public static readonly IReadOnlyDictionary ArgMatchersIncompatibleWithForAnyArgsMethodNames = new Dictionary + { + [ArgIsMethodName] = NSubstituteArgFullTypeName, + [ArgDoMethodName] = NSubstituteArgFullTypeName, + [ArgInvokeMethodName] = NSubstituteArgFullTypeName, + [ArgInvokeDelegateMethodName] = NSubstituteArgFullTypeName + }; + + public static readonly IReadOnlyDictionary ArgMatchersCompatIncompatibleWithForAnyArgsMethodNames = new Dictionary + { + [ArgIsMethodName] = NSubstituteArgCompatFullTypeName, + [ArgDoMethodName] = NSubstituteArgCompatFullTypeName, + [ArgInvokeMethodName] = NSubstituteArgCompatFullTypeName, + [ArgInvokeDelegateMethodName] = NSubstituteArgCompatFullTypeName + }; + public static readonly IReadOnlyDictionary InitialReEntryMethodNames = new Dictionary { [NSubstituteReturnsMethod] = NSubstituteSubstituteExtensionsFullTypeName, diff --git a/src/NSubstitute.Analyzers.Shared/Resources.Designer.cs b/src/NSubstitute.Analyzers.Shared/Resources.Designer.cs index ed819ac7..d35312c9 100644 --- a/src/NSubstitute.Analyzers.Shared/Resources.Designer.cs +++ b/src/NSubstitute.Analyzers.Shared/Resources.Designer.cs @@ -424,6 +424,24 @@ internal static string NonSubstitutableMemberArgumentMatcherUsageTitle { } } + internal static string WithAnyArgsArgumentMatcherUsageDescription { + get { + return ResourceManager.GetString("WithAnyArgsArgumentMatcherUsageDescription", resourceCulture); + } + } + + internal static string WithAnyArgsArgumentMatcherUsageMessageFormat { + get { + return ResourceManager.GetString("WithAnyArgsArgumentMatcherUsageMessageFormat", resourceCulture); + } + } + + internal static string WithAnyArgsArgumentMatcherUsageTitle { + get { + return ResourceManager.GetString("WithAnyArgsArgumentMatcherUsageTitle", resourceCulture); + } + } + internal static string ReceivedUsedInReceivedInOrderDescription { get { return ResourceManager.GetString("ReceivedUsedInReceivedInOrderDescription", resourceCulture); diff --git a/src/NSubstitute.Analyzers.Shared/Resources.resx b/src/NSubstitute.Analyzers.Shared/Resources.resx index e35f5169..fa9a0ee9 100644 --- a/src/NSubstitute.Analyzers.Shared/Resources.resx +++ b/src/NSubstitute.Analyzers.Shared/Resources.resx @@ -375,6 +375,19 @@ Argument matcher used with a non-virtual member of a class. The title of the diagnostic. + + + Argument matcher used with WithAnyArgs. This matcher may not be executed.. + An optional longer localizable description of the diagnostic. + + + Argument matcher used with WithAnyArgs. This matcher may not be executed.. + The format-able message the diagnostic displays. + + + Argument matcher used with WithAnyArgs. This matcher may not be executed.. + The title of the diagnostic. + Received-like method used in Received.InOrder block. @@ -418,4 +431,4 @@ Synchronous exception thrown from async method. - \ No newline at end of file + diff --git a/src/NSubstitute.Analyzers.VisualBasic/DiagnosticAnalyzers/SubstitutionNodeFinder.cs b/src/NSubstitute.Analyzers.VisualBasic/DiagnosticAnalyzers/SubstitutionNodeFinder.cs index 698d12a4..20361246 100644 --- a/src/NSubstitute.Analyzers.VisualBasic/DiagnosticAnalyzers/SubstitutionNodeFinder.cs +++ b/src/NSubstitute.Analyzers.VisualBasic/DiagnosticAnalyzers/SubstitutionNodeFinder.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; @@ -17,6 +18,7 @@ private SubstitutionNodeFinder() { } + // TODO replace with SyntaxVisitor or OperationVisitor protected override IEnumerable FindInvocations(SyntaxNodeAnalysisContext syntaxNodeContext, SyntaxNode argumentSyntax) { SyntaxNode body = null; @@ -91,9 +93,11 @@ protected override IEnumerable FindInvocations(SyntaxNodeAnalysisCon yield return invocationExpression; } } - else if (memberAccessExpressions.Any()) + + if (memberAccessExpressions.Any()) { - foreach (var memberAccessExpression in memberAccessExpressions) + var descendants = new HashSet(invocationExpressions.SelectMany(x => x.DescendantNodes())); + foreach (var memberAccessExpression in memberAccessExpressions.Where(x => !descendants.Contains(x))) { yield return memberAccessExpression; } diff --git a/src/NSubstitute.Analyzers.VisualBasic/DiagnosticAnalyzers/WithAnyArgsArgumentMatcherAnalyzer.cs b/src/NSubstitute.Analyzers.VisualBasic/DiagnosticAnalyzers/WithAnyArgsArgumentMatcherAnalyzer.cs new file mode 100644 index 00000000..3b8faccb --- /dev/null +++ b/src/NSubstitute.Analyzers.VisualBasic/DiagnosticAnalyzers/WithAnyArgsArgumentMatcherAnalyzer.cs @@ -0,0 +1,27 @@ +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.VisualBasic; +using Microsoft.CodeAnalysis.VisualBasic.Syntax; +using NSubstitute.Analyzers.Shared.DiagnosticAnalyzers; + +namespace NSubstitute.Analyzers.VisualBasic.DiagnosticAnalyzers; + +[DiagnosticAnalyzer(LanguageNames.VisualBasic)] +internal sealed class WithAnyArgsArgumentMatcherAnalyzer : AbstractWithAnyArgsArgumentMatcherAnalyzer +{ + internal static ImmutableHashSet MaybeAllowedAncestors { get; } = ImmutableHashSet.Create( + (int)SyntaxKind.InvocationExpression, + (int)SyntaxKind.ObjectCreationExpression, + (int)SyntaxKind.EqualsExpression, + (int)SyntaxKind.SimpleAssignmentStatement); + + public WithAnyArgsArgumentMatcherAnalyzer() + : base(VisualBasic.DiagnosticDescriptorsProvider.Instance, SubstitutionNodeFinder.Instance) + { + } + + protected override ImmutableHashSet MaybeAllowedArgMatcherAncestors { get; } = MaybeAllowedAncestors; + + protected override SyntaxKind InvocationExpressionKind { get; } = SyntaxKind.InvocationExpression; +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.Benchmarks/ConventionTests/BenchmarksConventionTests.cs b/tests/NSubstitute.Analyzers.Tests.Benchmarks/ConventionTests/BenchmarksConventionTests.cs index f03b1a71..3d6a3d4c 100644 --- a/tests/NSubstitute.Analyzers.Tests.Benchmarks/ConventionTests/BenchmarksConventionTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.Benchmarks/ConventionTests/BenchmarksConventionTests.cs @@ -20,6 +20,9 @@ public class BenchmarksConventionTests private static readonly Assembly[] AnalyzersAssemblies; private static readonly BenchmarkDescriptor[] BenchmarkDescriptors; + private static readonly IReadOnlyList SupportedLanguages = + new[] { LanguageNames.CSharp, LanguageNames.VisualBasic }; + static BenchmarksConventionTests() { var benchmarksAssembly = typeof(Program).Assembly; @@ -37,15 +40,24 @@ public void BenchmarksShouldBeDefinedForAllAnalyzers() } [Fact] - public void BenchmarksShouldProduceAllDiagnostics() + public async Task BenchmarksShouldProduceAllDiagnostics() { var allDiagnosticIds = BenchmarkDescriptors .SelectMany(benchmark => benchmark.Benchmark.Analyzer.SupportedDiagnostics.Select(diag => diag.Id)) .Distinct() .OrderBy(diag => diag) .ToList(); + var expectedDiagnosticIds = SupportedLanguages + .Select(language => new DiagnosticIdsWithLanguage(language, allDiagnosticIds)).ToList(); + + var producedDiagnosticIds = await GetProducedDiagnosticIds(); + + producedDiagnosticIds.Should().BeEquivalentTo(expectedDiagnosticIds, opts => opts.WithStrictOrdering()); + } - var producedDiagnostics = BenchmarkDescriptors.Select(async benchmark => + private async Task> GetProducedDiagnosticIds() + { + var producedDiagnostics = await BenchmarkDescriptors.ToAsyncEnumerable().SelectAwait(async benchmark => { var propertyInfo = benchmark.Property.DeclaringType.GetProperty( nameof(AbstractDiagnosticAnalyzersBenchmarks.Solution), @@ -53,18 +65,17 @@ public void BenchmarksShouldProduceAllDiagnostics() var solution = propertyInfo.GetValue(benchmark.DeclaringTypeInstance) as Solution; return await GetDiagnostics(solution, benchmark.Benchmark.Analyzer); - }).SelectMany(task => task.Result).ToList(); + }).ToListAsync(); - var producedDiagnosticIds = producedDiagnostics - .SelectMany(diag => diag.Select(x => x.Id)) - .Distinct() - .OrderBy(diag => diag) + return producedDiagnostics.GroupBy(diagnostic => diagnostic.Language).Select(grouping => + new DiagnosticIdsWithLanguage( + grouping.Key, + grouping.SelectMany(diagWithAnalyzer => diagWithAnalyzer.DiagnosticIds).Distinct().OrderBy(diagId => diagId).ToList())) + .OrderBy(diag => diag.Language) .ToList(); - - producedDiagnosticIds.Should().BeEquivalentTo(allDiagnosticIds); } - private async Task>> GetDiagnostics(Solution solution, DiagnosticAnalyzer analyzer) + private async Task GetDiagnostics(Solution solution, DiagnosticAnalyzer analyzer) { var diagnostics = new List>(); foreach (var project in solution.Projects) @@ -74,7 +85,7 @@ private async Task>> GetDiagnostics(Sol diagnostics.Add(await compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync()); } - return diagnostics; + return new DiagnosticsWithAnalyzer(analyzer, diagnostics.SelectMany(diag => diag).ToList()); } private static BenchmarkDescriptor[] GetAnalyzerBenchmarks(Assembly benchmarksAssembly) @@ -96,19 +107,15 @@ private static BenchmarkDescriptor[] GetAnalyzerBenchmarks(Assembly benchmarksAs return benchmarkAnalyzers; } - private class BenchmarkDescriptor - { - public FieldInfo Property { get; } - - public AnalyzerBenchmark Benchmark { get; } + private record BenchmarkDescriptor(FieldInfo Property, AnalyzerBenchmark Benchmark, AbstractDiagnosticAnalyzersBenchmarks DeclaringTypeInstance); - public AbstractDiagnosticAnalyzersBenchmarks DeclaringTypeInstance { get; } + private record DiagnosticsWithAnalyzer(DiagnosticAnalyzer Analyzer, IReadOnlyList Diagnostics) + { + public string Language { get; } = + Analyzer.GetType().GetCustomAttribute().Languages.Single(); - public BenchmarkDescriptor(FieldInfo property, AnalyzerBenchmark benchmark, AbstractDiagnosticAnalyzersBenchmarks declaringTypeInstance) - { - Property = property; - Benchmark = benchmark; - DeclaringTypeInstance = declaringTypeInstance; - } + public IReadOnlyList DiagnosticIds => Diagnostics.Select(diag => diag.Id).ToList(); } + + private record DiagnosticIdsWithLanguage(string Language, IReadOnlyList DiagnosticIds); } \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.Benchmarks/NSubstitute.Analyzers.Tests.Benchmarks.csproj b/tests/NSubstitute.Analyzers.Tests.Benchmarks/NSubstitute.Analyzers.Tests.Benchmarks.csproj index 87b33dfd..796fc888 100644 --- a/tests/NSubstitute.Analyzers.Tests.Benchmarks/NSubstitute.Analyzers.Tests.Benchmarks.csproj +++ b/tests/NSubstitute.Analyzers.Tests.Benchmarks/NSubstitute.Analyzers.Tests.Benchmarks.csproj @@ -7,8 +7,9 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + + diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberArgumentMatcherAnalyzerTests/NonSubstitutableMemberArgumentMatcherDiagnosticVerifier.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberArgumentMatcherAnalyzerTests/NonSubstitutableMemberArgumentMatcherDiagnosticVerifier.cs index 576d852b..9704dc2e 100644 --- a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberArgumentMatcherAnalyzerTests/NonSubstitutableMemberArgumentMatcherDiagnosticVerifier.cs +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberArgumentMatcherAnalyzerTests/NonSubstitutableMemberArgumentMatcherDiagnosticVerifier.cs @@ -133,7 +133,7 @@ public abstract class NonSubstitutableMemberArgumentMatcherDiagnosticVerifier : [CombinatoryTheory] [MemberData(nameof(CorrectlyUsedArgTestCasesWithoutDelegates))] - public abstract Task ReportsNoDiagnostics_WhenAssigningArgMatchersToSubstitutableMemberPrecededByReceivedLikeMethod(string receivedMethod, string arg); + public abstract Task ReportsDiagnostics_WhenAssigningInvalidArgMatchersToMemberPrecededByWithAnyArgsLikeMethod(string receivedMethod, string arg); public static IEnumerable MisusedArgTestCases { diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberArgumentMatcherAnalyzerTests/NonSubstitutableMemberArgumentMatcherTests.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberArgumentMatcherAnalyzerTests/NonSubstitutableMemberArgumentMatcherTests.cs index 7892d5fc..fd05da0b 100644 --- a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberArgumentMatcherAnalyzerTests/NonSubstitutableMemberArgumentMatcherTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/NonSubstitutableMemberArgumentMatcherAnalyzerTests/NonSubstitutableMemberArgumentMatcherTests.cs @@ -899,7 +899,7 @@ public void Test() "ReceivedWithAnyArgs()", "DidNotReceive()", "DidNotReceiveWithAnyArgs()")] - public override async Task ReportsNoDiagnostics_WhenAssigningArgMatchersToSubstitutableMemberPrecededByReceivedLikeMethod(string receivedMethod, string arg) + public override async Task ReportsDiagnostics_WhenAssigningInvalidArgMatchersToMemberPrecededByWithAnyArgsLikeMethod(string receivedMethod, string arg) { var source = $@"using System; using NSubstitute; diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ForAnyArgsArgumentMatcherDiagnosticVerifier.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ForAnyArgsArgumentMatcherDiagnosticVerifier.cs new file mode 100644 index 00000000..492f357e --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ForAnyArgsArgumentMatcherDiagnosticVerifier.cs @@ -0,0 +1,104 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using NSubstitute.Analyzers.CSharp; +using NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers; +using NSubstitute.Analyzers.Shared; +using NSubstitute.Analyzers.Shared.Settings; +using NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; +using Xunit; + +namespace NSubstitute.Analyzers.Tests.CSharp.DiagnosticAnalyzerTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public abstract class ForAnyArgsArgumentMatcherDiagnosticVerifier : CSharpDiagnosticVerifier, IForAnyArgsArgumentMatcherDiagnosticVerifier +{ + internal AnalyzersSettings Settings { get; set; } + + protected DiagnosticDescriptor WithAnyArgsArgumentMatcherUsage { get; } = DiagnosticDescriptors.WithAnyArgsArgumentMatcherUsage; + + protected override DiagnosticAnalyzer DiagnosticAnalyzer { get; } = new WithAnyArgsArgumentMatcherAnalyzer(); + + [CombinatoryTheory] + [MemberData(nameof(MisusedArgTestCasesWithoutDelegates))] + public abstract Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg); + + [CombinatoryTheory] + [MemberData(nameof(CorrectlyUsedArgTestCasesWithoutDelegates))] + public abstract Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg); + + [CombinatoryTheory] + [MemberData(nameof(ArgAnyTestCases))] + public abstract Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg); + + [CombinatoryTheory] + [MemberData(nameof(MisusedArgTestCasesWithoutDelegates))] + public abstract Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg); + + [CombinatoryTheory] + [MemberData(nameof(CorrectlyUsedArgTestCasesWithoutDelegates))] + public abstract Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg); + + [CombinatoryTheory] + [MemberData(nameof(ArgAnyTestCases))] + public abstract Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg); + + public static IEnumerable MisusedArgTestCases + { + get + { + yield return new object[] { "[|Arg.Is(1)|]" }; + yield return new object[] { "(int)[|Arg.Is(1)|]" }; + yield return new object[] { "[|Arg.Is(1)|] as int?" }; + yield return new object[] { "[|Arg.Compat.Is(1)|]" }; + yield return new object[] { "(int)[|Arg.Compat.Is(1)|]" }; + yield return new object[] { "[|Arg.Compat.Is(1)|] as int?" }; + yield return new object[] { "[|Arg.Do(__ => {})|]" }; + yield return new object[] { "[|Arg.Compat.Do(__ => {})|]" }; + yield return new object[] { "[|Arg.Invoke(1)|]" }; + yield return new object[] { "[|Arg.Compat.Invoke(1)|]" }; + yield return new object[] { "[|Arg.InvokeDelegate>()|]" }; + yield return new object[] { "[|Arg.Compat.InvokeDelegate>()|]" }; + } + } + + public static IEnumerable MisusedArgTestCasesWithoutDelegates => + MisusedArgTestCases.Where(arguments => arguments.All(argument => !argument.ToString().Contains("Invoke"))); + + public static IEnumerable ArgAnyTestCases => + CorrectlyUsedArgTestCases.Where(arguments => arguments.All(argument => argument.ToString().Contains("Any"))); + + public static IEnumerable CorrectlyUsedArgTestCases + { + get + { + yield return new object[] { "Arg.Any()" }; + yield return new object[] { "(int)Arg.Any()" }; + yield return new object[] { "Arg.Any() as int?" }; + yield return new object[] { "Arg.Compat.Any()" }; + yield return new object[] { "(int)Arg.Compat.Any()" }; + yield return new object[] { "Arg.Compat.Any() as int?" }; + yield return new object[] { "Arg.Is(1)" }; + yield return new object[] { "(int)Arg.Is(1)" }; + yield return new object[] { "Arg.Is(1) as int?" }; + yield return new object[] { "Arg.Is((int __) => __ > 0)" }; + yield return new object[] { "true ? Arg.Is((int __) => __ > 0) : 0" }; + yield return new object[] { "Arg.Compat.Is(1)" }; + yield return new object[] { "(int)Arg.Compat.Is(1)" }; + yield return new object[] { "Arg.Compat.Is(1) as int?" }; + yield return new object[] { "Arg.Compat.Is((int __) => __ > 0) " }; + yield return new object[] { "true ? Arg.Compat.Is((int __) => __ > 0) : 0" }; + yield return new object[] { "Arg.Do(__ => {})" }; + yield return new object[] { "Arg.Compat.Do(__ => {})" }; + yield return new object[] { "Arg.Invoke(1)" }; + yield return new object[] { "Arg.Compat.Invoke(1)" }; + yield return new object[] { "Arg.InvokeDelegate>()" }; + yield return new object[] { "Arg.Compat.InvokeDelegate>()" }; + } + } + + public static IEnumerable CorrectlyUsedArgTestCasesWithoutDelegates => + CorrectlyUsedArgTestCases.Where(arguments => arguments.All(argument => !argument.ToString().Contains("Invoke"))); +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReceivedAsExtensionMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReceivedAsExtensionMethodTests.cs new file mode 100644 index 00000000..ad65b932 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReceivedAsExtensionMethodTests.cs @@ -0,0 +1,330 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.CSharp.DiagnosticAnalyzerTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class ReceivedAsExtensionMethodTests : WithAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData( + "ReceivedWithAnyArgs(Quantity.None())", + "ReceivedWithAnyArgs()", + "DidNotReceiveWithAnyArgs()")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? Foo {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}.Foo = {arg}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "DidNotReceive()", + "Received(Quantity.None())", + "Received()")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithPropertyNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? Foo {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}.Foo = {arg}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ReceivedWithAnyArgs(Quantity.None())", + "ReceivedWithAnyArgs()", + "DidNotReceiveWithAnyArgs()")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? Foo {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}.Foo = {arg}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "DidNotReceive()", + "Received(Quantity.None())", + "Received()")] + public override async Task ReportsNoDiagnostics_WhenAssigningArgMatchersToMemberNotPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Action Foo {{ get; set; }} + Action this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}.Foo = {arg}; + substitute.{method}[1] = {arg}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ReceivedWithAnyArgs(Quantity.None())", + "ReceivedWithAnyArgs()", + "DidNotReceiveWithAnyArgs()")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}[1] = {arg}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "DidNotReceive()", + "Received(Quantity.None())", + "Received()")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}[1] = {arg}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ReceivedWithAnyArgs(Quantity.None())", + "ReceivedWithAnyArgs()", + "DidNotReceiveWithAnyArgs()")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}[1] = {arg}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "ReceivedWithAnyArgs(Quantity.None())", + "ReceivedWithAnyArgs()", + "DidNotReceiveWithAnyArgs()")] + public override async Task ReportsDiagnostics_WhenAssigningInvalidArgMatchersToMemberPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Action Foo {{ get; set; }} + Action this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}.Foo = {arg}; + substitute.{method}[1] = {arg}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "ReceivedWithAnyArgs(Quantity.None())", + "ReceivedWithAnyArgs()", + "DidNotReceiveWithAnyArgs()")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string receivedMethod, string arg) + { + var source = $@"using System; + using NSubstitute; + using NSubstitute.ReceivedExtensions; + + namespace MyNamespace + {{ + public interface Foo + {{ + void Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{receivedMethod}.Bar({arg}); + }} + }} + }}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "DidNotReceive()", + "Received(Quantity.None())", + "Received()")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; + using NSubstitute; + using NSubstitute.ReceivedExtensions; + + namespace MyNamespace + {{ + public interface Foo + {{ + void Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}.Bar({arg}); + }} + }} + }}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ReceivedWithAnyArgs(Quantity.None())", + "ReceivedWithAnyArgs()", + "DidNotReceiveWithAnyArgs()")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; + using NSubstitute; + using NSubstitute.ReceivedExtensions; + + namespace MyNamespace + {{ + public interface Foo + {{ + void Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}.Bar({arg}); + }} + }} + }}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReceivedAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReceivedAsOrdinaryMethodTests.cs new file mode 100644 index 00000000..d49a819c --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReceivedAsOrdinaryMethodTests.cs @@ -0,0 +1,376 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.CSharp.DiagnosticAnalyzerTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class ReceivedAsOrdinaryMethodTests : WithAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData( + "ReceivedExtensions.ReceivedWithAnyArgs(substitute, Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute: substitute, requiredQuantity: Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(requiredQuantity: Quantity.None(), substitute: substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute: substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute: substitute)")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? Foo {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {method}.Foo = {arg}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "SubstituteExtensions.DidNotReceive(substitute)", + "SubstituteExtensions.DidNotReceive(substitute: substitute)", + "ReceivedExtensions.Received(substitute, Quantity.None())", + "ReceivedExtensions.Received(substitute: substitute, requiredQuantity: Quantity.None())", + "ReceivedExtensions.Received(requiredQuantity: Quantity.None(), substitute: substitute)", + "SubstituteExtensions.Received(substitute)", + "SubstituteExtensions.Received(substitute: substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithPropertyNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? Foo {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {method}.Foo = {arg}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ReceivedExtensions.ReceivedWithAnyArgs(substitute, Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute: substitute, requiredQuantity: Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(requiredQuantity: Quantity.None(), substitute: substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute: substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute: substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? Foo {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {method}.Foo = {arg}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.DidNotReceive(substitute)", + "SubstituteExtensions.DidNotReceive(substitute: substitute)", + "ReceivedExtensions.Received(substitute, Quantity.None())", + "ReceivedExtensions.Received(substitute: substitute, requiredQuantity: Quantity.None())", + "ReceivedExtensions.Received(requiredQuantity: Quantity.None(), substitute: substitute)", + "SubstituteExtensions.Received(substitute)", + "SubstituteExtensions.Received(substitute: substitute)")] + public override async Task ReportsNoDiagnostics_WhenAssigningArgMatchersToMemberNotPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Action Foo {{ get; set; }} + Action this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {method}.Foo = {arg}; + {method}[1] = {arg}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ReceivedExtensions.ReceivedWithAnyArgs(substitute, Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute: substitute, requiredQuantity: Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(requiredQuantity: Quantity.None(), substitute: substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute: substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute: substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {method}[1] = {arg}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.DidNotReceive(substitute)", + "SubstituteExtensions.DidNotReceive(substitute: substitute)", + "ReceivedExtensions.Received(substitute, Quantity.None())", + "ReceivedExtensions.Received(substitute: substitute, requiredQuantity: Quantity.None())", + "ReceivedExtensions.Received(requiredQuantity: Quantity.None(), substitute: substitute)", + "SubstituteExtensions.Received(substitute)", + "SubstituteExtensions.Received(substitute: substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {method}[1] = {arg}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ReceivedExtensions.ReceivedWithAnyArgs(substitute, Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute: substitute, requiredQuantity: Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(requiredQuantity: Quantity.None(), substitute: substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute: substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute: substitute)")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {method}[1] = {arg}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "ReceivedExtensions.ReceivedWithAnyArgs(substitute, Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute: substitute, requiredQuantity: Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(requiredQuantity: Quantity.None(), substitute: substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute: substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute: substitute)")] + public override async Task ReportsDiagnostics_WhenAssigningInvalidArgMatchersToMemberPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Action Foo {{ get; set; }} + Action this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {method}.Foo = {arg}; + {method}[1] = {arg}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "ReceivedExtensions.ReceivedWithAnyArgs(substitute, Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute: substitute, requiredQuantity: Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(requiredQuantity: Quantity.None(), substitute: substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute: substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute: substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + void Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {method}.Bar({arg}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.DidNotReceive(substitute)", + "SubstituteExtensions.DidNotReceive(substitute: substitute)", + "ReceivedExtensions.Received(substitute, Quantity.None())", + "ReceivedExtensions.Received(substitute: substitute, requiredQuantity: Quantity.None())", + "ReceivedExtensions.Received(requiredQuantity: Quantity.None(), substitute: substitute)", + "SubstituteExtensions.Received(substitute)", + "SubstituteExtensions.Received(substitute: substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + void Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {method}.Bar({arg}); + }} + }} +}}"; + + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ReceivedExtensions.ReceivedWithAnyArgs(substitute, Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute: substitute, requiredQuantity: Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(requiredQuantity: Quantity.None(), substitute: substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute: substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute: substitute)")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + void Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {method}.Bar({arg}); + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } +} diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReturnsAsExtensionMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReturnsAsExtensionMethodTests.cs new file mode 100644 index 00000000..81770a03 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReturnsAsExtensionMethodTests.cs @@ -0,0 +1,166 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.CSharp.DiagnosticAnalyzerTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class ReturnsAsExtensionMethodTests : ForAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData("ReturnsForAnyArgs(null)", "ReturnsNullForAnyArgs()")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReturnsExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.Bar({arg}).{method}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData("Returns(null)", "ReturnsNull()", "ReturnsForAll((object)null)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReturnsExtensions; +using NSubstitute.Extensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.Bar({arg}).{method}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("ReturnsForAnyArgs(null)", "ReturnsNullForAnyArgs()")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReturnsExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.Bar({arg}).{method}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("ReturnsForAnyArgs(null)", "ReturnsNullForAnyArgs()")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReturnsExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object this[int? x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute[{arg}].{method}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData("Returns(null)", "ReturnsNull()", "ReturnsForAll((object)null)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReturnsExtensions; +using NSubstitute.Extensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object this[int? x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute[{arg}].{method}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("ReturnsForAnyArgs(null)", "ReturnsNullForAnyArgs()")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReturnsExtensions; +using NSubstitute.Extensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object this[int? x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute[{arg}].{method}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReturnsAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReturnsAsOrdinaryMethodTests.cs new file mode 100644 index 00000000..16c13407 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReturnsAsOrdinaryMethodTests.cs @@ -0,0 +1,202 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.CSharp.DiagnosticAnalyzerTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class ReturnsAsOrdinaryMethodTests : ForAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData( + "SubstituteExtensions.ReturnsForAnyArgs({0}, null)", + "SubstituteExtensions.ReturnsForAnyArgs(value: {0}, returnThis: null)", + "SubstituteExtensions.ReturnsForAnyArgs(returnThis: null, value: {0})", + "ReturnsExtensions.ReturnsNullForAnyArgs({0})", + "ReturnsExtensions.ReturnsNullForAnyArgs(value: {0})")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReturnsExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"substitute.Bar({arg})")}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "SubstituteExtensions.Returns({0}, null)", + "SubstituteExtensions.Returns(value: {0}, returnThis: null)", + "SubstituteExtensions.Returns(returnThis: null, value: {0})", + "ReturnsExtensions.ReturnsNull({0})", + "ReturnsExtensions.ReturnsNull(value: {0})", + "ReturnsForAllExtensions.ReturnsForAll({0}, (object)null)", + "ReturnsForAllExtensions.ReturnsForAll(substitute: {0}, returnThis: (object)null)", + "ReturnsForAllExtensions.ReturnsForAll(returnThis: (object)null, substitute: {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReturnsExtensions; +using NSubstitute.Extensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"substitute.Bar({arg})")}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.ReturnsForAnyArgs({0}, null)", + "SubstituteExtensions.ReturnsForAnyArgs(value: {0}, returnThis: null)", + "SubstituteExtensions.ReturnsForAnyArgs(returnThis: null, value: {0})", + "ReturnsExtensions.ReturnsNullForAnyArgs({0})", + "ReturnsExtensions.ReturnsNullForAnyArgs(value: {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReturnsExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"substitute.Bar({arg})")}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.ReturnsForAnyArgs({0}, null)", + "SubstituteExtensions.ReturnsForAnyArgs(value: {0}, returnThis: null)", + "SubstituteExtensions.ReturnsForAnyArgs(returnThis: null, value: {0})", + "ReturnsExtensions.ReturnsNullForAnyArgs({0})", + "ReturnsExtensions.ReturnsNullForAnyArgs(value: {0})")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReturnsExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object this[int? x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"substitute[{arg}]")}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "SubstituteExtensions.Returns({0}, null)", + "SubstituteExtensions.Returns(value: {0}, returnThis: null)", + "SubstituteExtensions.Returns(returnThis: null, value: {0})", + "ReturnsExtensions.ReturnsNull({0})", + "ReturnsExtensions.ReturnsNull(value: {0})", + "ReturnsForAllExtensions.ReturnsForAll({0}, (object)null)", + "ReturnsForAllExtensions.ReturnsForAll(substitute: {0}, returnThis: (object)null)", + "ReturnsForAllExtensions.ReturnsForAll(returnThis: (object)null, substitute: {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReturnsExtensions; +using NSubstitute.Extensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object this[int? x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"substitute[{arg}]")}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.ReturnsForAnyArgs({0}, null)", + "SubstituteExtensions.ReturnsForAnyArgs(value: {0}, returnThis: null)", + "SubstituteExtensions.ReturnsForAnyArgs(returnThis: null, value: {0})", + "ReturnsExtensions.ReturnsNullForAnyArgs({0})", + "ReturnsExtensions.ReturnsNullForAnyArgs(value: {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReturnsExtensions; +using NSubstitute.Extensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object this[int? x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"substitute[{arg}]")}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsExtensionMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsExtensionMethodTests.cs new file mode 100644 index 00000000..6576d609 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsExtensionMethodTests.cs @@ -0,0 +1,163 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.CSharp.DiagnosticAnalyzerTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class ThrowsAsExtensionMethodTests : ForAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData("ThrowsForAnyArgs()", "ThrowsForAnyArgs(new Exception())")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.Bar({arg}).{method}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData("Throws()", "Throws(new Exception())")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.Bar({arg}).{method}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("ThrowsForAnyArgs()", "ThrowsForAnyArgs(new Exception())")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.Bar({arg}).{method}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("ThrowsForAnyArgs()", "ThrowsForAnyArgs(new Exception())")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object this[int? x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute[{arg}].{method}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData("Throws()", "Throws(new Exception())")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object this[int? x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute[{arg}].{method}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("ThrowsForAnyArgs()", "ThrowsForAnyArgs(new Exception())")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object this[int? x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute[{arg}].{method}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsOrdinaryMethodTests.cs new file mode 100644 index 00000000..aa2ce5fe --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsOrdinaryMethodTests.cs @@ -0,0 +1,193 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.CSharp.DiagnosticAnalyzerTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class ThrowsAsOrdinaryMethodTests : ForAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData( + "ExceptionExtensions.ThrowsForAnyArgs({0})", + "ExceptionExtensions.ThrowsForAnyArgs(value: {0})", + "ExceptionExtensions.ThrowsForAnyArgs({0}, new Exception())", + "ExceptionExtensions.ThrowsForAnyArgs(value: {0}, ex: new Exception())", + "ExceptionExtensions.ThrowsForAnyArgs(ex: new Exception(), value: {0})")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"substitute.Bar({arg})")}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "ExceptionExtensions.Throws({0})", + "ExceptionExtensions.Throws(value: {0})", + "ExceptionExtensions.Throws({0}, new Exception())", + "ExceptionExtensions.Throws(value: {0}, ex: new Exception())", + "ExceptionExtensions.Throws(ex: new Exception(), value: {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"substitute.Bar({arg})")}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ExceptionExtensions.ThrowsForAnyArgs({0})", + "ExceptionExtensions.ThrowsForAnyArgs(value: {0})", + "ExceptionExtensions.ThrowsForAnyArgs({0}, new Exception())", + "ExceptionExtensions.ThrowsForAnyArgs(value: {0}, ex: new Exception())", + "ExceptionExtensions.ThrowsForAnyArgs(ex: new Exception(), value: {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"substitute.Bar({arg})")}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ExceptionExtensions.ThrowsForAnyArgs({0})", + "ExceptionExtensions.ThrowsForAnyArgs(value: {0})", + "ExceptionExtensions.ThrowsForAnyArgs({0}, new Exception())", + "ExceptionExtensions.ThrowsForAnyArgs(value: {0}, ex: new Exception())", + "ExceptionExtensions.ThrowsForAnyArgs(ex: new Exception(), value: {0})")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object this[int? x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"substitute[{arg}]")}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "ExceptionExtensions.Throws({0})", + "ExceptionExtensions.Throws(value: {0})", + "ExceptionExtensions.Throws({0}, new Exception())", + "ExceptionExtensions.Throws(value: {0}, ex: new Exception())", + "ExceptionExtensions.Throws(ex: new Exception(), value: {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object this[int? x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"substitute[{arg}]")}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ExceptionExtensions.ThrowsForAnyArgs({0})", + "ExceptionExtensions.ThrowsForAnyArgs(value: {0})", + "ExceptionExtensions.ThrowsForAnyArgs({0}, new Exception())", + "ExceptionExtensions.ThrowsForAnyArgs(value: {0}, ex: new Exception())", + "ExceptionExtensions.ThrowsForAnyArgs(ex: new Exception(), value: {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + object this[int? x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"substitute[{arg}]")}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsyncAsExtensionMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsyncAsExtensionMethodTests.cs new file mode 100644 index 00000000..31410a98 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsyncAsExtensionMethodTests.cs @@ -0,0 +1,169 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.CSharp.DiagnosticAnalyzerTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class ThrowsAsyncAsExtensionMethodTests : ForAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData("ThrowsAsyncForAnyArgs()", "ThrowsAsyncForAnyArgs(new Exception())")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + Task Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.Bar({arg}).{method}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData("ThrowsAsync()", "ThrowsAsync(new Exception())")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + Task Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.Bar({arg}).{method}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("ThrowsAsyncForAnyArgs()", "ThrowsAsyncForAnyArgs(new Exception())")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + Task Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.Bar({arg}).{method}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("ThrowsAsyncForAnyArgs()", "ThrowsAsyncForAnyArgs(new Exception())")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + Task this[int? x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute[{arg}].{method}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData("ThrowsAsync()", "ThrowsAsync(new Exception())")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + Task this[int? x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute[{arg}].{method}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("ThrowsAsyncForAnyArgs()", "ThrowsAsyncForAnyArgs(new Exception())")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + Task this[int? x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute[{arg}].{method}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsyncOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsyncOrdinaryMethodTests.cs new file mode 100644 index 00000000..4ebdd594 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsyncOrdinaryMethodTests.cs @@ -0,0 +1,199 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.CSharp.DiagnosticAnalyzerTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class ThrowsAsyncOrdinaryMethodTests : ForAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData( + "ExceptionExtensions.ThrowsAsyncForAnyArgs({0})", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(value: {0})", + "ExceptionExtensions.ThrowsAsyncForAnyArgs({0}, new Exception())", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(value: {0}, ex: new Exception())", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(ex: new Exception(), value: {0})")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + Task Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"substitute.Bar({arg})")}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "ExceptionExtensions.ThrowsAsync({0})", + "ExceptionExtensions.ThrowsAsync(value: {0})", + "ExceptionExtensions.ThrowsAsync({0}, new Exception())", + "ExceptionExtensions.ThrowsAsync(value: {0}, ex: new Exception())", + "ExceptionExtensions.ThrowsAsync(ex: new Exception(), value: {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + Task Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"substitute.Bar({arg})")}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ExceptionExtensions.ThrowsAsyncForAnyArgs({0})", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(value: {0})", + "ExceptionExtensions.ThrowsAsyncForAnyArgs({0}, new Exception())", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(value: {0}, ex: new Exception())", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(ex: new Exception(), value: {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + Task Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"substitute.Bar({arg})")}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ExceptionExtensions.ThrowsAsyncForAnyArgs({0})", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(value: {0})", + "ExceptionExtensions.ThrowsAsyncForAnyArgs({0}, new Exception())", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(value: {0}, ex: new Exception())", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(ex: new Exception(), value: {0})")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + Task this[int? x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"substitute[{arg}]")}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "ExceptionExtensions.ThrowsAsync({0})", + "ExceptionExtensions.ThrowsAsync(value: {0})", + "ExceptionExtensions.ThrowsAsync({0}, new Exception())", + "ExceptionExtensions.ThrowsAsync(value: {0}, ex: new Exception())", + "ExceptionExtensions.ThrowsAsync(ex: new Exception(), value: {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + Task this[int? x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"substitute[{arg}]")}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ExceptionExtensions.ThrowsAsyncForAnyArgs({0})", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(value: {0})", + "ExceptionExtensions.ThrowsAsyncForAnyArgs({0}, new Exception())", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(value: {0}, ex: new Exception())", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(ex: new Exception(), value: {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using System.Threading.Tasks; +using NSubstitute; +using NSubstitute.ExceptionExtensions; + +namespace MyNamespace +{{ + public interface Foo + {{ + Task this[int? x] {{ get; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"substitute[{arg}]")}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/WhenAsExtensionMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/WhenAsExtensionMethodTests.cs new file mode 100644 index 00000000..27ad31ef --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/WhenAsExtensionMethodTests.cs @@ -0,0 +1,310 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.CSharp.DiagnosticAnalyzerTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class WhenAsExtensionMethodTests : WithAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData("WhenForAnyArgs")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? Foo {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}(x => x.Foo = {arg}); + substitute.{method}(x => {{ x.Foo = {arg}; }}); + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData("When")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithPropertyNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? Foo {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}(x => x.Foo = {arg}); + substitute.{method}(x => {{ x.Foo = {arg}; }}); + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("WhenForAnyArgs")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? Foo {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}(x => x.Foo = {arg}); + substitute.{method}(x => {{ x.Foo = {arg}; }}); + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("When")] + public override async Task ReportsNoDiagnostics_WhenAssigningArgMatchersToMemberNotPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Action Foo {{ get; set; }} + Action this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}(x => x.Foo = {arg}); + substitute.{method}(x => {{ x.Foo = {arg}; }}); + substitute.{method}(x => x[1] = {arg}); + substitute.{method}(x => {{ x[1] = {arg}; }}); + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("WhenForAnyArgs")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}(x => x[{arg}] = {arg}); + substitute.{method}(x => {{ x[{arg}] = {arg}; }}); + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("When")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}(x => x[{arg}] = {arg}); + substitute.{method}(x => {{ x[{arg}] = {arg}; }}); + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("WhenForAnyArgs")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}(x => x[{arg}] = {arg}); + substitute.{method}(x => {{ x[{arg}] = {arg}; }}); + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData("WhenForAnyArgs")] + public override async Task ReportsDiagnostics_WhenAssigningInvalidArgMatchersToMemberPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Action Foo {{ get; set; }} + Action this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}(x => x.Foo = {arg}); + substitute.{method}(x => {{ x.Foo = {arg}; }}); + substitute.{method}(x => x[1] = {arg}); + substitute.{method}(x => {{ x[1] = {arg}; }}); + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData("WhenForAnyArgs")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string receivedMethod, string arg) + { + var source = $@"using System; + using NSubstitute; + using NSubstitute.ReceivedExtensions; + + namespace MyNamespace + {{ + public interface Foo + {{ + void Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{receivedMethod}(x => x.Bar({arg})); + substitute.{receivedMethod}(x => {{ x.Bar({arg}); }}); + }} + }} + }}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("When")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; + using NSubstitute; + using NSubstitute.ReceivedExtensions; + + namespace MyNamespace + {{ + public interface Foo + {{ + void Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}(x => x.Bar({arg})); + substitute.{method}(x => {{ x.Bar({arg}); }}); + }} + }} + }}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("WhenForAnyArgs")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; + using NSubstitute; + using NSubstitute.ReceivedExtensions; + + namespace MyNamespace + {{ + public interface Foo + {{ + void Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + substitute.{method}(x => x.Bar({arg})); + substitute.{method}(x => {{ x.Bar({arg}); }}); + }} + }} + }}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/WhenAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/WhenAsOrdinaryMethodTests.cs new file mode 100644 index 00000000..3841c093 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/WhenAsOrdinaryMethodTests.cs @@ -0,0 +1,343 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.CSharp.DiagnosticAnalyzerTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class WhenAsOrdinaryMethodTests : WithAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData( + "SubstituteExtensions.WhenForAnyArgs(substitute, {0})", + "SubstituteExtensions.WhenForAnyArgs(substitute: substitute, substituteCall: {0})", + "SubstituteExtensions.WhenForAnyArgs(substituteCall: {0}, substitute: substitute)")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? Foo {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"x => x.Foo = {arg}")}; + {string.Format(method, $"x => {{ x.Foo = {arg}; }}")}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "SubstituteExtensions.When(substitute, {0})", + "SubstituteExtensions.When(substitute: substitute, substituteCall: {0})", + "SubstituteExtensions.When(substituteCall: {0}, substitute: substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithPropertyNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? Foo {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"x => x.Foo = {arg}")}; + {string.Format(method, $"x => {{ x.Foo = {arg}; }}")}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.WhenForAnyArgs(substitute, {0})", + "SubstituteExtensions.WhenForAnyArgs(substitute: substitute, substituteCall: {0})", + "SubstituteExtensions.WhenForAnyArgs(substituteCall: {0}, substitute: substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? Foo {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"x => x.Foo = {arg}")}; + {string.Format(method, $"x => {{ x.Foo = {arg}; }}")}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.When(substitute, {0})", + "SubstituteExtensions.When(substitute: substitute, substituteCall: {0})", + "SubstituteExtensions.When(substituteCall: {0}, substitute: substitute)")] + public override async Task ReportsNoDiagnostics_WhenAssigningArgMatchersToMemberNotPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Action Foo {{ get; set; }} + Action this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"x => x.Foo = {arg}")}; + {string.Format(method, $"x => {{ x.Foo = {arg}; }}")}; + {string.Format(method, $"x => x[1] = {arg}")}; + {string.Format(method, $"x => {{ x[1] = {arg}; }}")}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.WhenForAnyArgs(substitute, {0})", + "SubstituteExtensions.WhenForAnyArgs(substitute: substitute, substituteCall: {0})", + "SubstituteExtensions.WhenForAnyArgs(substituteCall: {0}, substitute: substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"x => x[{arg}] = {arg}")}; + {string.Format(method, $"x => {{ x[{arg}] = {arg}; }}")}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.When(substitute, {0})", + "SubstituteExtensions.When(substitute: substitute, substituteCall: {0})", + "SubstituteExtensions.When(substituteCall: {0}, substitute: substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"x => x[{arg}] = {arg}")}; + {string.Format(method, $"x => {{ x[{arg}] = {arg}; }}")}; + }} + }} +}}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.WhenForAnyArgs(substitute, {0})", + "SubstituteExtensions.WhenForAnyArgs(substitute: substitute, substituteCall: {0})", + "SubstituteExtensions.WhenForAnyArgs(substituteCall: {0}, substitute: substitute)")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + int? this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"x => x[{arg}] = {arg}")}; + {string.Format(method, $"x => {{ x[{arg}] = {arg}; }}")}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "SubstituteExtensions.WhenForAnyArgs(substitute, {0})", + "SubstituteExtensions.WhenForAnyArgs(substitute: substitute, substituteCall: {0})", + "SubstituteExtensions.WhenForAnyArgs(substituteCall: {0}, substitute: substitute)")] + public override async Task ReportsDiagnostics_WhenAssigningInvalidArgMatchersToMemberPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg) + { + var source = $@"using System; +using NSubstitute; +using NSubstitute.ReceivedExtensions; + +namespace MyNamespace +{{ + public interface IFoo + {{ + Action Foo {{ get; set; }} + Action this[int? x] {{ get; set; }} + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"x => x.Foo = {arg}")}; + {string.Format(method, $"x => {{ x.Foo = {arg}; }}")}; + {string.Format(method, $"x => x[1] = {arg}")}; + {string.Format(method, $"x => {{ x[1] = {arg}; }}")}; + }} + }} +}}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "SubstituteExtensions.WhenForAnyArgs(substitute, {0})", + "SubstituteExtensions.WhenForAnyArgs(substitute: substitute, substituteCall: {0})", + "SubstituteExtensions.WhenForAnyArgs(substituteCall: {0}, substitute: substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; + using NSubstitute; + using NSubstitute.ReceivedExtensions; + + namespace MyNamespace + {{ + public interface Foo + {{ + void Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"x => x.Bar({arg})")}; + {string.Format(method, $"x => {{ x.Bar({arg}); }}")}; + }} + }} + }}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.When(substitute, {0})", + "SubstituteExtensions.When(substitute: substitute, substituteCall: {0})", + "SubstituteExtensions.When(substituteCall: {0}, substitute: substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; + using NSubstitute; + using NSubstitute.ReceivedExtensions; + + namespace MyNamespace + {{ + public interface Foo + {{ + void Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"x => x.Bar({arg})")}; + {string.Format(method, $"x => {{ x.Bar({arg}); }}")}; + }} + }} + }}"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.WhenForAnyArgs(substitute, {0})", + "SubstituteExtensions.WhenForAnyArgs(substitute: substitute, substituteCall: {0})", + "SubstituteExtensions.WhenForAnyArgs(substituteCall: {0}, substitute: substitute)")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"using System; + using NSubstitute; + using NSubstitute.ReceivedExtensions; + + namespace MyNamespace + {{ + public interface Foo + {{ + void Bar(int? x); + }} + + public class FooTests + {{ + public void Test() + {{ + var substitute = Substitute.For(); + {string.Format(method, $"x => x.Bar({arg})")}; + {string.Format(method, $"x => {{ x.Bar({arg}); }}")}; + }} + }} + }}"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/WithAnyArgsArgumentMatcherDiagnosticVerifier.cs b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/WithAnyArgsArgumentMatcherDiagnosticVerifier.cs new file mode 100644 index 00000000..efcac171 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.CSharp/DiagnosticAnalyzerTests/WithAnyArgsArgumentMatcherAnalyzerTests/WithAnyArgsArgumentMatcherDiagnosticVerifier.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; +using NSubstitute.Analyzers.CSharp.DiagnosticAnalyzers; +using NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; +using Xunit; + +namespace NSubstitute.Analyzers.Tests.CSharp.DiagnosticAnalyzerTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public abstract class WithAnyArgsArgumentMatcherDiagnosticVerifier : ForAnyArgsArgumentMatcherDiagnosticVerifier, IWithAnyArgsArgumentMatcherDiagnosticVerifier +{ + protected override DiagnosticAnalyzer DiagnosticAnalyzer { get; } = new WithAnyArgsArgumentMatcherAnalyzer(); + + [CombinatoryTheory] + [MemberData(nameof(MisusedArgTestCasesWithoutDelegates))] + public abstract Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg); + + [CombinatoryTheory] + [MemberData(nameof(CorrectlyUsedArgTestCasesWithoutDelegates))] + public abstract Task ReportsNoDiagnostics_WhenUsingArgMatchersWithPropertyNotCombinedWithAnyArgsLikeMethod(string method, string arg); + + [CombinatoryTheory] + [MemberData(nameof(ArgAnyTestCases))] + public abstract Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg); + + [CombinatoryTheory] + [MemberData(nameof(CorrectlyUsedArgTestCasesDelegates))] + public abstract Task ReportsNoDiagnostics_WhenAssigningArgMatchersToMemberNotPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg); + + [CombinatoryTheory] + [MemberData(nameof(MisusedArgTestCasesDelegates))] + public abstract Task ReportsDiagnostics_WhenAssigningInvalidArgMatchersToMemberPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg); + + public static IEnumerable MisusedArgTestCasesDelegates => + MisusedArgTestCases.Where(arguments => arguments.All(argument => argument.ToString().Contains("Invoke"))); + + public static IEnumerable CorrectlyUsedArgTestCasesDelegates => + CorrectlyUsedArgTestCases.Where(arguments => arguments.All(argument => argument.ToString().Contains("Invoke"))); +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.Shared/DiagnosticAnalyzers/IForAnyArgsArgumentMatcherDiagnosticVerifier.cs b/tests/NSubstitute.Analyzers.Tests.Shared/DiagnosticAnalyzers/IForAnyArgsArgumentMatcherDiagnosticVerifier.cs new file mode 100644 index 00000000..5f8abf2f --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.Shared/DiagnosticAnalyzers/IForAnyArgsArgumentMatcherDiagnosticVerifier.cs @@ -0,0 +1,18 @@ +using System.Threading.Tasks; + +namespace NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers; + +public interface IForAnyArgsArgumentMatcherDiagnosticVerifier +{ + Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg); + + Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg); + + Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg); + + Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg); + + Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg); + + Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg); +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.Shared/DiagnosticAnalyzers/INonSubstitutableMemberArgumentMatcherDiagnosticVerifier.cs b/tests/NSubstitute.Analyzers.Tests.Shared/DiagnosticAnalyzers/INonSubstitutableMemberArgumentMatcherDiagnosticVerifier.cs index 305eb863..6cd49e65 100644 --- a/tests/NSubstitute.Analyzers.Tests.Shared/DiagnosticAnalyzers/INonSubstitutableMemberArgumentMatcherDiagnosticVerifier.cs +++ b/tests/NSubstitute.Analyzers.Tests.Shared/DiagnosticAnalyzers/INonSubstitutableMemberArgumentMatcherDiagnosticVerifier.cs @@ -54,7 +54,7 @@ public interface INonSubstitutableMemberArgumentMatcherDiagnosticVerifier Task ReportsDiagnostics_WhenDirectlyAssigningNotAllowedArgMatchersToMember(string arg); - Task ReportsNoDiagnostics_WhenAssigningArgMatchersToSubstitutableMemberPrecededByReceivedLikeMethod(string receivedMethod, string arg); + Task ReportsDiagnostics_WhenAssigningInvalidArgMatchersToMemberPrecededByWithAnyArgsLikeMethod(string receivedMethod, string arg); Task ReportsDiagnostics_WhenAssigningArgMatchersToNonSubstitutableMember_InWhenLikeMethod(string whenMethod, string arg); diff --git a/tests/NSubstitute.Analyzers.Tests.Shared/DiagnosticAnalyzers/IWithAnyArgsArgumentMatcherDiagnosticVerifier.cs b/tests/NSubstitute.Analyzers.Tests.Shared/DiagnosticAnalyzers/IWithAnyArgsArgumentMatcherDiagnosticVerifier.cs new file mode 100644 index 00000000..231e3306 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.Shared/DiagnosticAnalyzers/IWithAnyArgsArgumentMatcherDiagnosticVerifier.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; + +namespace NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers; + +public interface IWithAnyArgsArgumentMatcherDiagnosticVerifier : IForAnyArgsArgumentMatcherDiagnosticVerifier +{ + Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg); + + Task ReportsNoDiagnostics_WhenUsingArgMatchersWithPropertyNotCombinedWithAnyArgsLikeMethod(string method, string arg); + + Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg); + + Task ReportsNoDiagnostics_WhenAssigningArgMatchersToMemberNotPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg); + + Task ReportsDiagnostics_WhenAssigningInvalidArgMatchersToMemberPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg); +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberArgumentMatcherAnalyzerTests/NonSubstitutableMemberArgumentMatcherDiagnosticVerifier.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberArgumentMatcherAnalyzerTests/NonSubstitutableMemberArgumentMatcherDiagnosticVerifier.cs index 986edfff..baaf7e79 100644 --- a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberArgumentMatcherAnalyzerTests/NonSubstitutableMemberArgumentMatcherDiagnosticVerifier.cs +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberArgumentMatcherAnalyzerTests/NonSubstitutableMemberArgumentMatcherDiagnosticVerifier.cs @@ -125,7 +125,7 @@ public abstract class NonSubstitutableMemberArgumentMatcherDiagnosticVerifier : [CombinatoryTheory] [MemberData(nameof(CorrectlyUsedArgTestCasesWithoutDelegates))] - public abstract Task ReportsNoDiagnostics_WhenAssigningArgMatchersToSubstitutableMemberPrecededByReceivedLikeMethod(string receivedMethod, string arg); + public abstract Task ReportsDiagnostics_WhenAssigningInvalidArgMatchersToMemberPrecededByWithAnyArgsLikeMethod(string receivedMethod, string arg); [CombinatoryTheory] [MemberData(nameof(MisusedArgTestCasesWithoutDelegates))] diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberArgumentMatcherAnalyzerTests/NonSubstitutableMemberArgumentMatcherTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberArgumentMatcherAnalyzerTests/NonSubstitutableMemberArgumentMatcherTests.cs index 34e4f0cb..13c2a394 100644 --- a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberArgumentMatcherAnalyzerTests/NonSubstitutableMemberArgumentMatcherTests.cs +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberArgumentMatcherAnalyzerTests/NonSubstitutableMemberArgumentMatcherTests.cs @@ -825,7 +825,7 @@ End Namespace "ReceivedWithAnyArgs()", "DidNotReceive()", "DidNotReceiveWithAnyArgs()")] - public override async Task ReportsNoDiagnostics_WhenAssigningArgMatchersToSubstitutableMemberPrecededByReceivedLikeMethod(string receivedMethod, string arg) + public override async Task ReportsDiagnostics_WhenAssigningInvalidArgMatchersToMemberPrecededByWithAnyArgsLikeMethod(string receivedMethod, string arg) { var source = $@"Imports System Imports NSubstitute diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberWhenAnalyzerTests/NonSubstitutableMemberWhenDiagnosticVerifier.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberWhenAnalyzerTests/NonSubstitutableMemberWhenDiagnosticVerifier.cs index 6318b2b6..d216f7b9 100644 --- a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberWhenAnalyzerTests/NonSubstitutableMemberWhenDiagnosticVerifier.cs +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/NonSubstitutableMemberWhenAnalyzerTests/NonSubstitutableMemberWhenDiagnosticVerifier.cs @@ -187,12 +187,14 @@ Dim x as Integer [InlineData( @"Sub(sb As Foo) Dim x = [|sb.Bar|] - End Sub", "Friend member Bar can not be intercepted without InternalsVisibleToAttribute.")] + End Sub", + "Friend member Bar can not be intercepted without InternalsVisibleToAttribute.")] [InlineData(@"Sub(sb As Foo) [|sb.FooBar(Arg.Any(Of Integer)())|]", "Friend member FooBar can not be intercepted without InternalsVisibleToAttribute.")] [InlineData( @"Sub(sb As Foo) Dim x = [|sb(Arg.Any(Of Integer)())|] - End Sub", "Friend member Item can not be intercepted without InternalsVisibleToAttribute.")] + End Sub", + "Friend member Item can not be intercepted without InternalsVisibleToAttribute.")] public abstract Task ReportsDiagnostics_WhenSettingValueForInternalVirtualMember_AndInternalsVisibleToNotApplied(string method, string call, string message); [CombinatoryTheory] @@ -211,12 +213,14 @@ Dim x as Integer [InlineData( @"Sub(sb As Foo) Dim x = [|sb.Bar|] - End Sub", "Friend member Bar can not be intercepted without InternalsVisibleToAttribute.")] + End Sub", + "Friend member Bar can not be intercepted without InternalsVisibleToAttribute.")] [InlineData(@"Sub(sb As Foo) [|sb.FooBar(Arg.Any(Of Integer)())|]", "Friend member FooBar can not be intercepted without InternalsVisibleToAttribute.")] [InlineData( @"Sub(sb As Foo) Dim x = [|sb(Arg.Any(Of Integer)())|] - End Sub", "Friend member Item can not be intercepted without InternalsVisibleToAttribute.")] + End Sub", + "Friend member Item can not be intercepted without InternalsVisibleToAttribute.")] public abstract Task ReportsDiagnostics_WhenSettingValueForInternalVirtualMember_AndInternalsVisibleToAppliedToWrongAssembly(string method, string call, string message); [CombinatoryTheory] diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ForAnyArgsArgumentMatcherDiagnosticVerifier.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ForAnyArgsArgumentMatcherDiagnosticVerifier.cs new file mode 100644 index 00000000..cd26a072 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ForAnyArgsArgumentMatcherDiagnosticVerifier.cs @@ -0,0 +1,137 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using NSubstitute.Analyzers.Shared; +using NSubstitute.Analyzers.Shared.Settings; +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.WithAnyArgsArgumentMatcherAnalyzerTests; + +public abstract class ForAnyArgsArgumentMatcherDiagnosticVerifier : VisualBasicDiagnosticVerifier, IForAnyArgsArgumentMatcherDiagnosticVerifier +{ + internal AnalyzersSettings Settings { get; set; } + + protected DiagnosticDescriptor WithAnyArgsArgumentMatcherUsage { get; } = DiagnosticDescriptors.WithAnyArgsArgumentMatcherUsage; + + protected override DiagnosticAnalyzer DiagnosticAnalyzer { get; } = new WithAnyArgsArgumentMatcherAnalyzer(); + + [CombinatoryTheory] + [MemberData(nameof(MisusedArgTestCasesWithoutDelegates))] + public abstract Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg); + + [CombinatoryTheory] + [MemberData(nameof(CorrectlyUsedArgTestCasesWithoutDelegates))] + public abstract Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg); + + [CombinatoryTheory] + [MemberData(nameof(ArgAnyTestCases))] + public abstract Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg); + + [CombinatoryTheory] + [MemberData(nameof(MisusedArgTestCasesWithoutDelegates))] + public abstract Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg); + + [CombinatoryTheory] + [MemberData(nameof(CorrectlyUsedArgTestCasesWithoutDelegates))] + public abstract Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg); + + [CombinatoryTheory] + [MemberData(nameof(ArgAnyTestCases))] + public abstract Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg); + + public static IEnumerable MisusedArgTestCases + { + get { return MisusedArgs.Select(argArray => argArray.Select(arg => arg).ToArray()); } + } + + public static IEnumerable MisusedArgTestCasesWithoutDelegates => + MisusedArgTestCases.Where(arguments => arguments.All(argument => !argument.ToString().Contains("Invoke"))); + + public static IEnumerable ArgAnyTestCases => + CorrectlyUsedArgTestCases.Where(arguments => arguments.All(argument => argument.ToString().Contains("Any"))); + + public static IEnumerable MisusedArgTestCasesDelegates => + MisusedArgTestCases.Where(arguments => arguments.All(argument => argument.ToString().Contains("Invoke"))); + + public static IEnumerable CorrectlyUsedArgTestCases + { + get + { + yield return new object[] { "Arg.Any(Of Integer)()" }; + yield return new object[] { "TryCast(Arg.Any(Of Integer)(), Object)" }; + yield return new object[] { "CType(Arg.Any(Of Integer)(), Integer)" }; + yield return new object[] { "DirectCast(Arg.Any(Of Integer)(), Integer)" }; + yield return new object[] { "Arg.Compat.Any(Of Integer)()" }; + yield return new object[] { "TryCast(Arg.Compat.Any(Of Integer)(), Object)" }; + yield return new object[] { "CType(Arg.Compat.Any(Of Integer)(), Integer)" }; + yield return new object[] { "DirectCast(Arg.Compat.Any(Of Integer)(), Integer)" }; + yield return new object[] { "Arg.Is(1)" }; + yield return new object[] { "TryCast(Arg.Is(1), Object)" }; + yield return new object[] { "CType(Arg.Is(1), Integer)" }; + yield return new object[] { "DirectCast(Arg.Is(1), Integer)" }; + yield return new object[] { "Arg.Is(Function(ByVal __ As Integer) __ > 0)" }; + yield return new object[] { "If(True, Arg.Is(Function(ByVal __ As Integer) __ > 0), 0)" }; + yield return new object[] { "Arg.Compat.Is(1)" }; + yield return new object[] { "TryCast(Arg.Compat.Is(1), Object)" }; + yield return new object[] { "CType(Arg.Compat.Is(1), Integer)" }; + yield return new object[] { "DirectCast(Arg.Compat.Is(1), Integer)" }; + yield return new object[] { "Arg.Compat.Is(Function(ByVal __ As Integer) __ > 0)" }; + yield return new object[] { "If(True, Arg.Compat.Is(Function(ByVal __ As Integer) __ > 0), 0)" }; + yield return new object[] { "Arg.Invoke(1)" }; + yield return new object[] { "Arg.Compat.Invoke(1)" }; + yield return new object[] { "Arg.InvokeDelegate(Of Action(Of Integer))()" }; + yield return new object[] { "Arg.Compat.InvokeDelegate(Of Action(Of Integer))()" }; + yield return new object[] + { + @"Arg.Do(Of Integer)(Function(doValue) +End Function)" + }; + yield return new object[] + { + @"Arg.Compat.Do(Of Integer)(Function(doValue) +End Function)" + }; + } + } + + public static IEnumerable CorrectlyUsedArgTestCasesWithoutDelegates => CorrectlyUsedArgTestCases.Where(arguments => + arguments.All(argument => !argument.ToString().Contains("Invoke"))); + + public static IEnumerable CorrectlyUsedArgTestCasesDelegates => + CorrectlyUsedArgTestCases.Where(arguments => arguments.All(argument => argument.ToString().Contains("Invoke"))); + + public static IEnumerable MisusedArgs + { + get + { + yield return new[] { "[|Arg.Is(1)|]" }; + yield return new[] { "TryCast([|Arg.Is(1)|], Object)" }; + yield return new[] { "CType([|Arg.Is(1)|], Integer)" }; + yield return new[] { "DirectCast([|Arg.Is(1)|], Integer)" }; + yield return new[] { "[|Arg.Compat.Is(1)|]" }; + yield return new[] { "TryCast([|Arg.Compat.Is(1)|], Object)" }; + yield return new[] { "CType([|Arg.Compat.Is(1)|], Integer)" }; + yield return new[] { "DirectCast([|Arg.Compat.Is(1)|], Integer)" }; + yield return new[] { "[|Arg.Invoke(1)|]" }; + yield return new[] { "[|Arg.Compat.Invoke(1)|]" }; + yield return new[] { "[|Arg.InvokeDelegate(Of Action(Of Integer))()|]" }; + yield return new[] { "[|Arg.Compat.InvokeDelegate(Of Action(Of Integer))()|]" }; + yield return new[] + { + @"[|Arg.Do(Of Integer)(Function(doValue) +End Function)|]" + }; + yield return new[] + { + @"[|Arg.Compat.Do(Of Integer)(Function(doValue) +End Function)|]" + }; + } + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReceivedAsExtensionMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReceivedAsExtensionMethodTests.cs new file mode 100644 index 00000000..0c2696a3 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReceivedAsExtensionMethodTests.cs @@ -0,0 +1,297 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class ReceivedAsExtensionMethodTests : WithAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData( + "ReceivedWithAnyArgs(Quantity.None())", + "ReceivedWithAnyArgs()", + "DidNotReceiveWithAnyArgs()")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.{method}.Foo = {arg} + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "DidNotReceive()", + "Received(Quantity.None())", + "Received()")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithPropertyNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.{method}.Foo = {arg} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ReceivedWithAnyArgs(Quantity.None())", + "ReceivedWithAnyArgs()", + "DidNotReceiveWithAnyArgs()")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.{method}.Foo = {arg} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "DidNotReceive()", + "Received(Quantity.None())", + "Received()")] + public override async Task ReportsNoDiagnostics_WhenAssigningArgMatchersToMemberNotPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Action(Of Integer) + Default Property Item(ByVal x As Integer?) As Action(Of Integer) + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.{method}.Foo = {arg} + substitute.{method}(1) = {arg} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ReceivedWithAnyArgs(Quantity.None())", + "ReceivedWithAnyArgs()", + "DidNotReceiveWithAnyArgs()")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Default Property Item(ByVal x As Integer?) As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.{method}(1) = {arg} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "DidNotReceive()", + "Received(Quantity.None())", + "Received()")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Default Property Item(ByVal x As Integer?) As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.{method}(1) = {arg} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ReceivedWithAnyArgs(Quantity.None())", + "ReceivedWithAnyArgs()", + "DidNotReceiveWithAnyArgs()")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Default Property Item(ByVal x As Integer?) As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.{method}(1) = {arg} + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "ReceivedWithAnyArgs(Quantity.None())", + "ReceivedWithAnyArgs()", + "DidNotReceiveWithAnyArgs()")] + public override async Task ReportsDiagnostics_WhenAssigningInvalidArgMatchersToMemberPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Action(Of Integer) + Default Property Item(ByVal x As Integer?) As Action(Of Integer) + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.{method}.Foo = {arg} + substitute.{method}(1) = {arg} + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "ReceivedWithAnyArgs(Quantity.None())", + "ReceivedWithAnyArgs()", + "DidNotReceiveWithAnyArgs()")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace +Interface Foo + Sub Bar(ByVal x As Integer?) +End Interface + +Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.{method}.Bar({arg}) + End Sub +End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "DidNotReceive()", + "Received(Quantity.None())", + "Received()")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface Foo + Sub Bar(ByVal x As Integer?) + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.{method}.Bar({arg}) + End Sub + End Class +End Namespace"; + + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ReceivedWithAnyArgs(Quantity.None())", + "ReceivedWithAnyArgs()", + "DidNotReceiveWithAnyArgs()")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface Foo + Sub Bar(ByVal x As Integer?) + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.{method}.Bar({arg}) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReceivedAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReceivedAsOrdinaryMethodTests.cs new file mode 100644 index 00000000..617f6552 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReceivedAsOrdinaryMethodTests.cs @@ -0,0 +1,341 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class ReceivedAsOrdinaryMethodTests : WithAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData( + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute:= substitute)", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute, Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute:= substitute, requiredQuantity:= Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(requiredQuantity:= Quantity.None(), substitute:= substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute:= substitute)")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + {method}.Foo = {arg} + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "SubstituteExtensions.DidNotReceive(substitute)", + "SubstituteExtensions.DidNotReceive(substitute:= substitute)", + "ReceivedExtensions.Received(substitute, Quantity.None())", + "ReceivedExtensions.Received(substitute:= substitute, requiredQuantity:= Quantity.None())", + "ReceivedExtensions.Received(requiredQuantity:= Quantity.None(), substitute:= substitute)", + "SubstituteExtensions.Received(substitute)", + "SubstituteExtensions.Received(substitute:= substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithPropertyNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + {method}.Foo = {arg} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute:= substitute)", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute, Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute:= substitute, requiredQuantity:= Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(requiredQuantity:= Quantity.None(), substitute:= substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute:= substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + {method}.Foo = {arg} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.DidNotReceive(substitute)", + "SubstituteExtensions.DidNotReceive(substitute:= substitute)", + "ReceivedExtensions.Received(substitute, Quantity.None())", + "ReceivedExtensions.Received(substitute:= substitute, requiredQuantity:= Quantity.None())", + "ReceivedExtensions.Received(requiredQuantity:= Quantity.None(), substitute:= substitute)", + "SubstituteExtensions.Received(substitute)", + "SubstituteExtensions.Received(substitute:= substitute)")] + public override async Task ReportsNoDiagnostics_WhenAssigningArgMatchersToMemberNotPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Action(Of Integer) + Default Property Item(ByVal x As Integer?) As Action(Of Integer) + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + {method}.Foo = {arg} + {method}(1) = {arg} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute:= substitute)", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute, Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute:= substitute, requiredQuantity:= Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(requiredQuantity:= Quantity.None(), substitute:= substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute:= substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Default Property Item(ByVal x As Integer?) As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + {method}(1) = {arg} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.DidNotReceive(substitute)", + "SubstituteExtensions.DidNotReceive(substitute:= substitute)", + "ReceivedExtensions.Received(substitute, Quantity.None())", + "ReceivedExtensions.Received(substitute:= substitute, requiredQuantity:= Quantity.None())", + "ReceivedExtensions.Received(requiredQuantity:= Quantity.None(), substitute:= substitute)", + "SubstituteExtensions.Received(substitute)", + "SubstituteExtensions.Received(substitute:= substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Default Property Item(ByVal x As Integer?) As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + {method}(1) = {arg} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute:= substitute)", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute, Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute:= substitute, requiredQuantity:= Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(requiredQuantity:= Quantity.None(), substitute:= substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute:= substitute)")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Default Property Item(ByVal x As Integer?) As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + {method}(1) = {arg} + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute:= substitute)", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute, Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute:= substitute, requiredQuantity:= Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(requiredQuantity:= Quantity.None(), substitute:= substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute:= substitute)")] + public override async Task ReportsDiagnostics_WhenAssigningInvalidArgMatchersToMemberPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Action(Of Integer) + Default Property Item(ByVal x As Integer?) As Action(Of Integer) + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + {method}.Foo = {arg} + {method}(1) = {arg} + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute:= substitute)", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute, Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute:= substitute, requiredQuantity:= Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(requiredQuantity:= Quantity.None(), substitute:= substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute:= substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace +Interface Foo + Sub Bar(ByVal x As Integer?) +End Interface + +Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {method}.Bar({arg}) + End Sub +End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.DidNotReceive(substitute)", + "SubstituteExtensions.DidNotReceive(substitute:= substitute)", + "ReceivedExtensions.Received(substitute, Quantity.None())", + "ReceivedExtensions.Received(substitute:= substitute, requiredQuantity:= Quantity.None())", + "ReceivedExtensions.Received(requiredQuantity:= Quantity.None(), substitute:= substitute)", + "SubstituteExtensions.Received(substitute)", + "SubstituteExtensions.Received(substitute:= substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface Foo + Sub Bar(ByVal x As Integer?) + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {method}.Bar({arg}) + End Sub + End Class +End Namespace"; + + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute)", + "SubstituteExtensions.DidNotReceiveWithAnyArgs(substitute:= substitute)", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute, Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(substitute:= substitute, requiredQuantity:= Quantity.None())", + "ReceivedExtensions.ReceivedWithAnyArgs(requiredQuantity:= Quantity.None(), substitute:= substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute)", + "SubstituteExtensions.ReceivedWithAnyArgs(substitute:= substitute)")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface Foo + Sub Bar(ByVal x As Integer?) + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {method}.Bar({arg}) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReturnsAsExtensionMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReturnsAsExtensionMethodTests.cs new file mode 100644 index 00000000..a33cd0d9 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReturnsAsExtensionMethodTests.cs @@ -0,0 +1,149 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class ReturnsAsExtensionMethodTests : ForAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData("ReturnsForAnyArgs(CType(Nothing, Foo))", "ReturnsNullForAnyArgs()")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReturnsExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar({arg}).{method} + End Sub + End Class +End Namespace"; + + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData("Returns(CType(Nothing, Foo))", "ReturnsNull()", "ReturnsForAll(CType(Nothing, Foo))")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReturnsExtensions +Imports NSubstitute.Extensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar({arg}).{method} + End Sub + End Class +End Namespace"; + + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("ReturnsForAnyArgs(CType(Nothing, Foo))", "ReturnsNullForAnyArgs()")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReturnsExtensions +Imports NSubstitute.Extensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar({arg}).{method} + End Sub + End Class +End Namespace"; + + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("ReturnsForAnyArgs(CType(Nothing, Foo))", "ReturnsNullForAnyArgs()")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReturnsExtensions + +Namespace MyNamespace + Interface Foo + Default ReadOnly Property Item(ByVal x As Integer?) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute({arg}).{method} + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData("Returns(CType(Nothing, Foo))", "ReturnsNull()", "ReturnsForAll(CType(Nothing, Foo))")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReturnsExtensions +Imports NSubstitute.Extensions + +Namespace MyNamespace + Interface Foo + Default ReadOnly Property Item(ByVal x As Integer?) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute({arg}).{method} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("ReturnsForAnyArgs(CType(Nothing, Foo))", "ReturnsNullForAnyArgs()")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReturnsExtensions +Imports NSubstitute.Extensions + +Namespace MyNamespace + Interface Foo + Default ReadOnly Property Item(ByVal x As Integer?) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute({arg}).{method} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReturnsAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReturnsAsOrdinaryMethodTests.cs new file mode 100644 index 00000000..911f7bf6 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ReturnsAsOrdinaryMethodTests.cs @@ -0,0 +1,185 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class ReturnsAsOrdinaryMethodTests : ForAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData( + "SubstituteExtensions.ReturnsForAnyArgs({0}, CType(Nothing, Foo))", + "SubstituteExtensions.ReturnsForAnyArgs(value:= {0}, returnThis:= CType(Nothing, Foo))", + "SubstituteExtensions.ReturnsForAnyArgs(returnThis:= CType(Nothing, Foo), value:= {0})", + "ReturnsExtensions.ReturnsNullForAnyArgs({0})", + "ReturnsExtensions.ReturnsNullForAnyArgs(value:= {0})")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReturnsExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $"substitute.Bar({arg})")} + End Sub + End Class +End Namespace"; + + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "SubstituteExtensions.Returns({0}, CType(Nothing, Foo))", + "SubstituteExtensions.Returns(value:= {0}, returnThis:= CType(Nothing, Foo))", + "SubstituteExtensions.Returns(returnThis:= CType(Nothing, Foo), value:= {0})", + "ReturnsExtensions.ReturnsNull({0})", + "ReturnsExtensions.ReturnsNull(value:= {0})", + "ReturnsForAllExtensions.ReturnsForAll({0}, CType(Nothing, Foo))", + "ReturnsForAllExtensions.ReturnsForAll(substitute:= {0}, returnThis:= CType(Nothing, Foo))", + "ReturnsForAllExtensions.ReturnsForAll(returnThis:= CType(Nothing, Foo), substitute:= {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReturnsExtensions +Imports NSubstitute.Extensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $"substitute.Bar({arg})")} + End Sub + End Class +End Namespace"; + + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.ReturnsForAnyArgs({0}, CType(Nothing, Foo))", + "SubstituteExtensions.ReturnsForAnyArgs(value:= {0}, returnThis:= CType(Nothing, Foo))", + "SubstituteExtensions.ReturnsForAnyArgs(returnThis:= CType(Nothing, Foo), value:= {0})", + "ReturnsExtensions.ReturnsNullForAnyArgs({0})", + "ReturnsExtensions.ReturnsNullForAnyArgs(value:= {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReturnsExtensions +Imports NSubstitute.Extensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $"substitute.Bar({arg})")} + End Sub + End Class +End Namespace"; + + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.ReturnsForAnyArgs({0}, CType(Nothing, Foo))", + "SubstituteExtensions.ReturnsForAnyArgs(value:= {0}, returnThis:= CType(Nothing, Foo))", + "SubstituteExtensions.ReturnsForAnyArgs(returnThis:= CType(Nothing, Foo), value:= {0})", + "ReturnsExtensions.ReturnsNullForAnyArgs({0})", + "ReturnsExtensions.ReturnsNullForAnyArgs(value:= {0})")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReturnsExtensions + +Namespace MyNamespace + Interface Foo + Default ReadOnly Property Item(ByVal x As Integer?) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $"substitute({arg})")} + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "SubstituteExtensions.Returns({0}, CType(Nothing, Foo))", + "SubstituteExtensions.Returns(value:= {0}, returnThis:= CType(Nothing, Foo))", + "SubstituteExtensions.Returns(returnThis:= CType(Nothing, Foo), value:= {0})", + "ReturnsExtensions.ReturnsNull({0})", + "ReturnsExtensions.ReturnsNull(value:= {0})", + "ReturnsForAllExtensions.ReturnsForAll({0}, CType(Nothing, Foo))", + "ReturnsForAllExtensions.ReturnsForAll(substitute:= {0}, returnThis:= CType(Nothing, Foo))", + "ReturnsForAllExtensions.ReturnsForAll(returnThis:= CType(Nothing, Foo), substitute:= {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReturnsExtensions +Imports NSubstitute.Extensions + +Namespace MyNamespace + Interface Foo + Default ReadOnly Property Item(ByVal x As Integer?) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $"substitute({arg})")} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.ReturnsForAnyArgs({0}, CType(Nothing, Foo))", + "SubstituteExtensions.ReturnsForAnyArgs(value:= {0}, returnThis:= CType(Nothing, Foo))", + "SubstituteExtensions.ReturnsForAnyArgs(returnThis:= CType(Nothing, Foo), value:= {0})", + "ReturnsExtensions.ReturnsNullForAnyArgs({0})", + "ReturnsExtensions.ReturnsNullForAnyArgs(value:= {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReturnsExtensions +Imports NSubstitute.Extensions + +Namespace MyNamespace + Interface Foo + Default ReadOnly Property Item(ByVal x As Integer?) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $"substitute({arg})")} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsExtensionMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsExtensionMethodTests.cs new file mode 100644 index 00000000..43b10a4e --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsExtensionMethodTests.cs @@ -0,0 +1,146 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class ThrowsAsExtensionMethodTests : ForAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData("ThrowsForAnyArgs(Of Exception)()", "ThrowsForAnyArgs(New Exception())")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer?) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar({arg}).{method} + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData("Throws(Of Exception)()", "Throws(New Exception())")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer?) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar({arg}).{method} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("ThrowsForAnyArgs(Of Exception)()", "ThrowsForAnyArgs(New Exception())")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer?) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar({arg}).{method} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("ThrowsForAnyArgs(Of Exception)()", "ThrowsForAnyArgs(New Exception())")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Default ReadOnly Property Item(ByVal x As Integer?) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute({arg}).{method} + End Sub + End Class +End Namespace"; + + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData("Throws(Of Exception)()", "Throws(New Exception())")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Default ReadOnly Property Item(ByVal x As Integer?) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute({arg}).{method} + End Sub + End Class +End Namespace"; + + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("ThrowsForAnyArgs(Of Exception)()", "ThrowsForAnyArgs(New Exception())")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Default ReadOnly Property Item(ByVal x As Integer?) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute({arg}).{method} + End Sub + End Class +End Namespace"; + + await VerifyNoDiagnostic(source); + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsOrdinaryMethodTests.cs new file mode 100644 index 00000000..31e60446 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsOrdinaryMethodTests.cs @@ -0,0 +1,175 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class ThrowsAsOrdinaryMethodTests : ForAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData( + "ExceptionExtensions.ThrowsForAnyArgs(Of Exception)({0})", + "ExceptionExtensions.ThrowsForAnyArgs(Of Exception)(value:= {0})", + "ExceptionExtensions.ThrowsForAnyArgs({0}, New Exception())", + "ExceptionExtensions.ThrowsForAnyArgs(value:= {0}, ex:= New Exception())", + "ExceptionExtensions.ThrowsForAnyArgs(ex:= New Exception(), value:= {0})")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer?) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $"substitute.Bar({arg})")} + End Sub + End Class +End Namespace"; + + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "ExceptionExtensions.Throws(Of Exception)({0})", + "ExceptionExtensions.Throws(Of Exception)(value:= {0})", + "ExceptionExtensions.Throws({0}, New Exception())", + "ExceptionExtensions.Throws(value:= {0}, ex:= New Exception())", + "ExceptionExtensions.Throws(ex:= New Exception(), value:= {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer?) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $"substitute.Bar({arg})")} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ExceptionExtensions.ThrowsForAnyArgs(Of Exception)({0})", + "ExceptionExtensions.ThrowsForAnyArgs(Of Exception)(value:= {0})", + "ExceptionExtensions.ThrowsForAnyArgs({0}, New Exception())", + "ExceptionExtensions.ThrowsForAnyArgs(value:= {0}, ex:= New Exception())", + "ExceptionExtensions.ThrowsForAnyArgs(ex:= New Exception(), value:= {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer?) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $"substitute.Bar({arg})")} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ExceptionExtensions.ThrowsForAnyArgs(Of Exception)({0})", + "ExceptionExtensions.ThrowsForAnyArgs(Of Exception)(value:= {0})", + "ExceptionExtensions.ThrowsForAnyArgs({0}, New Exception())", + "ExceptionExtensions.ThrowsForAnyArgs(value:= {0}, ex:= New Exception())", + "ExceptionExtensions.ThrowsForAnyArgs(ex:= New Exception(), value:= {0})")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Default ReadOnly Property Item(ByVal x As Integer?) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $"substitute({arg})")} + End Sub + End Class +End Namespace"; + + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "ExceptionExtensions.Throws(Of Exception)({0})", + "ExceptionExtensions.Throws(Of Exception)(value:= {0})", + "ExceptionExtensions.Throws({0}, New Exception())", + "ExceptionExtensions.Throws(value:= {0}, ex:= New Exception())", + "ExceptionExtensions.Throws(ex:= New Exception(), value:= {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Default ReadOnly Property Item(ByVal x As Integer?) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $"substitute({arg})")} + End Sub + End Class +End Namespace"; + + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ExceptionExtensions.ThrowsForAnyArgs(Of Exception)({0})", + "ExceptionExtensions.ThrowsForAnyArgs(Of Exception)(value:= {0})", + "ExceptionExtensions.ThrowsForAnyArgs({0}, New Exception())", + "ExceptionExtensions.ThrowsForAnyArgs(value:= {0}, ex:= New Exception())", + "ExceptionExtensions.ThrowsForAnyArgs(ex:= New Exception(), value:= {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Default ReadOnly Property Item(ByVal x As Integer?) As Foo + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $"substitute({arg})")} + End Sub + End Class +End Namespace"; + + await VerifyNoDiagnostic(source); + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsyncAsExtensionMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsyncAsExtensionMethodTests.cs new file mode 100644 index 00000000..7027f414 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsyncAsExtensionMethodTests.cs @@ -0,0 +1,152 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class ThrowsAsyncAsExtensionMethodTests : ForAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData("ThrowsAsyncForAnyArgs(Of Exception)()", "ThrowsAsyncForAnyArgs(New Exception())")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer?) As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar({arg}).{method} + End Sub + End Class +End Namespace +"; + + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData("ThrowsAsync(Of Exception)()", "ThrowsAsync(New Exception())")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer?) As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar({arg}).{method} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("ThrowsAsyncForAnyArgs(Of Exception)()", "ThrowsAsyncForAnyArgs(New Exception())")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer?) As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.Bar({arg}).{method} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("ThrowsAsyncForAnyArgs(Of Exception)()", "ThrowsAsyncForAnyArgs(New Exception())")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Default ReadOnly Property Item(ByVal x As Integer?) As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute({arg}).{method} + End Sub + End Class +End Namespace"; + + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData("ThrowsAsync(Of Exception)()", "ThrowsAsync(New Exception())")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Default ReadOnly Property Item(ByVal x As Integer?) As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute({arg}).{method} + End Sub + End Class +End Namespace"; + + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("ThrowsAsyncForAnyArgs(Of Exception)()", "ThrowsAsyncForAnyArgs(New Exception())")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Default ReadOnly Property Item(ByVal x As Integer?) As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute({arg}).{method} + End Sub + End Class +End Namespace"; + + await VerifyNoDiagnostic(source); + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsyncAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsyncAsOrdinaryMethodTests.cs new file mode 100644 index 00000000..b73f9955 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/ThrowsAsyncAsOrdinaryMethodTests.cs @@ -0,0 +1,181 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class ThrowsAsyncAsOrdinaryMethodTests : ForAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData( + "ExceptionExtensions.ThrowsAsyncForAnyArgs(Of Exception)({0})", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(Of Exception)(value:= {0})", + "ExceptionExtensions.ThrowsAsyncForAnyArgs({0}, New Exception())", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(value:= {0}, ex:= New Exception())", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(ex:= New Exception(), value:= {0})")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer?) As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $"substitute.Bar({arg})")} + End Sub + End Class +End Namespace"; + + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "ExceptionExtensions.ThrowsAsync(Of Exception)({0})", + "ExceptionExtensions.ThrowsAsync(Of Exception)(value:= {0})", + "ExceptionExtensions.ThrowsAsync({0}, New Exception())", + "ExceptionExtensions.ThrowsAsync(value:= {0}, ex:= New Exception())", + "ExceptionExtensions.ThrowsAsync(ex:= New Exception(), value:= {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer?) As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $"substitute.Bar({arg})")} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ExceptionExtensions.ThrowsAsyncForAnyArgs(Of Exception)({0})", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(Of Exception)(value:= {0})", + "ExceptionExtensions.ThrowsAsyncForAnyArgs({0}, New Exception())", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(value:= {0}, ex:= New Exception())", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(ex:= New Exception(), value:= {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Function Bar(ByVal x As Integer?) As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $"substitute.Bar({arg})")} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ExceptionExtensions.ThrowsAsyncForAnyArgs(Of Exception)({0})", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(Of Exception)(value:= {0})", + "ExceptionExtensions.ThrowsAsyncForAnyArgs({0}, New Exception())", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(value:= {0}, ex:= New Exception())", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(ex:= New Exception(), value:= {0})")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Default ReadOnly Property Item(ByVal x As Integer?) As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $"substitute({arg})")} + End Sub + End Class +End Namespace"; + + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "ExceptionExtensions.ThrowsAsync(Of Exception)({0})", + "ExceptionExtensions.ThrowsAsync(Of Exception)(value:= {0})", + "ExceptionExtensions.ThrowsAsync({0}, New Exception())", + "ExceptionExtensions.ThrowsAsync(value:= {0}, ex:= New Exception())", + "ExceptionExtensions.ThrowsAsync(ex:= New Exception(), value:= {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Default ReadOnly Property Item(ByVal x As Integer?) As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $"substitute({arg})")} + End Sub + End Class +End Namespace"; + + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "ExceptionExtensions.ThrowsAsyncForAnyArgs(Of Exception)({0})", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(Of Exception)(value:= {0})", + "ExceptionExtensions.ThrowsAsyncForAnyArgs({0}, New Exception())", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(value:= {0}, ex:= New Exception())", + "ExceptionExtensions.ThrowsAsyncForAnyArgs(ex:= New Exception(), value:= {0})")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports System.Threading.Tasks +Imports NSubstitute +Imports NSubstitute.ExceptionExtensions + +Namespace MyNamespace + Interface Foo + Default ReadOnly Property Item(ByVal x As Integer?) As Task + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $"substitute({arg})")} + End Sub + End Class +End Namespace"; + + await VerifyNoDiagnostic(source); + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/WhenAsExtensionMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/WhenAsExtensionMethodTests.cs new file mode 100644 index 00000000..201f6ee0 --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/WhenAsExtensionMethodTests.cs @@ -0,0 +1,290 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class WhenAsExtensionMethodTests : WithAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData("WhenForAnyArgs")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.{method}(Function(x) + x.Foo = {arg} + End Function) + End Sub + End Class +End Namespace"; + + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData("When")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithPropertyNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.{method}(Function(x) + x.Foo = {arg} + End Function) + End Sub + End Class +End Namespace"; + + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("WhenForAnyArgs")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.{method}(Function(x) + x.Foo = {arg} + End Function) + End Sub + End Class +End Namespace"; + + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("When")] + public override async Task ReportsNoDiagnostics_WhenAssigningArgMatchersToMemberNotPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Action(Of Integer) + Default Property Item(ByVal x As Integer?) As Action(Of Integer) + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.{method}(Function(x) + x.Foo = {arg} + End Function) + substitute.{method}(Function(x) + x(1) = {arg} + End Function) + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("WhenForAnyArgs")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Default Property Item(ByVal x As Integer?) As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.{method}(Function(x) + x({arg}) = {arg} + End Function) + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("When")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Default Property Item(ByVal x As Integer?) As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.{method}(Function(x) + x({arg}) = {arg} + End Function) + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("WhenForAnyArgs")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Default Property Item(ByVal x As Integer?) As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.{method}(Function(x) + x({arg}) = {arg} + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData("WhenForAnyArgs")] + public override async Task ReportsDiagnostics_WhenAssigningInvalidArgMatchersToMemberPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Action(Of Integer) + Default Property Item(ByVal x As Integer?) As Action(Of Integer) + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + substitute.{method}(Function(x) + x.Foo = {arg} + End Function) + substitute.{method}(Function(x) + x(1) = {arg} + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData("WhenForAnyArgs")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface Foo + Sub Bar(ByVal x As Integer?) + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.{method}(Function(x) + x.Bar({arg}) + End Function) + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("When")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface Foo + Sub Bar(ByVal x As Integer?) + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.{method}(Function(x) + x.Bar({arg}) + End Function) + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData("WhenForAnyArgs")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface Foo + Sub Bar(ByVal x As Integer?) + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + substitute.{method}(Function(x) + x.Bar({arg}) + End Function) + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/WhenAsOrdinaryMethodTests.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/WhenAsOrdinaryMethodTests.cs new file mode 100644 index 00000000..4d84ab0b --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/WhenAsOrdinaryMethodTests.cs @@ -0,0 +1,323 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public class WhenAsOrdinaryMethodTests : WithAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryData( + "SubstituteExtensions.WhenForAnyArgs(substitute, {0})", + "SubstituteExtensions.WhenForAnyArgs(substitute:= substitute, substituteCall:= {0})", + "SubstituteExtensions.WhenForAnyArgs(substituteCall:= {0}, substitute:= substitute)")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + {string.Format(method, $@"Function(x) + x.Foo = {arg} + End Function")} + End Sub + End Class +End Namespace"; + + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "SubstituteExtensions.When(substitute, {0})", + "SubstituteExtensions.When(substitute:= substitute, substituteCall:= {0})", + "SubstituteExtensions.When(substituteCall:= {0}, substitute:= substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithPropertyNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + {string.Format(method, $@"Function(x) + x.Foo = {arg} + End Function")} + End Sub + End Class +End Namespace"; + + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.WhenForAnyArgs(substitute, {0})", + "SubstituteExtensions.WhenForAnyArgs(substitute:= substitute, substituteCall:= {0})", + "SubstituteExtensions.WhenForAnyArgs(substituteCall:= {0}, substitute:= substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + {string.Format(method, $@"Function(x) + x.Foo = {arg} + End Function")} + End Sub + End Class +End Namespace"; + + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.When(substitute, {0})", + "SubstituteExtensions.When(substitute:= substitute, substituteCall:= {0})", + "SubstituteExtensions.When(substituteCall:= {0}, substitute:= substitute)")] + public override async Task ReportsNoDiagnostics_WhenAssigningArgMatchersToMemberNotPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Action(Of Integer) + Default Property Item(ByVal x As Integer?) As Action(Of Integer) + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + {string.Format(method, $@"Function(x) + x.Foo = {arg} + End Function")} + {string.Format(method, $@"Function(x) + x(1) = {arg} + End Function")} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.WhenForAnyArgs(substitute, {0})", + "SubstituteExtensions.WhenForAnyArgs(substitute:= substitute, substituteCall:= {0})", + "SubstituteExtensions.WhenForAnyArgs(substituteCall:= {0}, substitute:= substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Default Property Item(ByVal x As Integer?) As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + {string.Format(method, $@"Function(x) + x({arg}) = {arg} + End Function")} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.When(substitute, {0})", + "SubstituteExtensions.When(substitute:= substitute, substituteCall:= {0})", + "SubstituteExtensions.When(substituteCall:= {0}, substitute:= substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithIndexerNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Default Property Item(ByVal x As Integer?) As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + {string.Format(method, $@"Function(x) + x({arg}) = {arg} + End Function")} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.WhenForAnyArgs(substitute, {0})", + "SubstituteExtensions.WhenForAnyArgs(substitute:= substitute, substituteCall:= {0})", + "SubstituteExtensions.WhenForAnyArgs(substituteCall:= {0}, substitute:= substitute)")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithIndexerCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Default Property Item(ByVal x As Integer?) As Integer? + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + {string.Format(method, $@"Function(x) + x(1) = {arg} + End Function")} + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "SubstituteExtensions.WhenForAnyArgs(substitute, {0})", + "SubstituteExtensions.WhenForAnyArgs(substitute:= substitute, substituteCall:= {0})", + "SubstituteExtensions.WhenForAnyArgs(substituteCall:= {0}, substitute:= substitute)")] + public override async Task ReportsDiagnostics_WhenAssigningInvalidArgMatchersToMemberPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface IFoo + Property Foo As Action(Of Integer) + Default Property Item(ByVal x As Integer?) As Action(Of Integer) + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of IFoo)() + {string.Format(method, $@"Function(x) + x.Foo = {arg} + End Function")} + {string.Format(method, $@"Function(x) + x(1) = {arg} + End Function")} + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } + + [CombinatoryData( + "SubstituteExtensions.WhenForAnyArgs(substitute, {0})", + "SubstituteExtensions.WhenForAnyArgs(substitute:= substitute, substituteCall:= {0})", + "SubstituteExtensions.WhenForAnyArgs(substituteCall:= {0}, substitute:= substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface Foo + Sub Bar(ByVal x As Integer?) + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $@"Function(x) + x.Bar({arg}) + End Function")} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.When(substitute, {0})", + "SubstituteExtensions.When(substitute:= substitute, substituteCall:= {0})", + "SubstituteExtensions.When(substituteCall:= {0}, substitute:= substitute)")] + public override async Task ReportsNoDiagnostics_WhenUsingArgMatchersWithInvocationNotCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface Foo + Sub Bar(ByVal x As Integer?) + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $@"Function(x) + x.Bar({arg}) + End Function")} + End Sub + End Class +End Namespace +"; + await VerifyNoDiagnostic(source); + } + + [CombinatoryData( + "SubstituteExtensions.WhenForAnyArgs(substitute, {0})", + "SubstituteExtensions.WhenForAnyArgs(substitute:= substitute, substituteCall:= {0})", + "SubstituteExtensions.WhenForAnyArgs(substituteCall:= {0}, substitute:= substitute)")] + public override async Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithInvocationCombinedWithAnyArgsLikeMethod(string method, string arg) + { + var source = $@"Imports System +Imports NSubstitute +Imports NSubstitute.ReceivedExtensions + +Namespace MyNamespace + Interface Foo + Sub Bar(ByVal x As Integer?) + End Interface + + Public Class FooTests + Public Sub Test() + Dim substitute = NSubstitute.Substitute.[For](Of Foo)() + {string.Format(method, $@"Function(x) + x.Bar({arg}) + End Function")} + End Sub + End Class +End Namespace +"; + await VerifyDiagnostic(source, WithAnyArgsArgumentMatcherUsage); + } +} \ No newline at end of file diff --git a/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/WithAnyArgsArgumentMatcherDiagnosticVerifier.cs b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/WithAnyArgsArgumentMatcherDiagnosticVerifier.cs new file mode 100644 index 00000000..a533607f --- /dev/null +++ b/tests/NSubstitute.Analyzers.Tests.VisualBasic/DiagnosticAnalyzersTests/WithAnyArgsArgumentMatcherAnalyzerTests/WithAnyArgsArgumentMatcherDiagnosticVerifier.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers; +using NSubstitute.Analyzers.Tests.Shared.Extensibility; +using Xunit; + +namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.WithAnyArgsArgumentMatcherAnalyzerTests; + +public abstract class WithAnyArgsArgumentMatcherDiagnosticVerifier : ForAnyArgsArgumentMatcherDiagnosticVerifier, IWithAnyArgsArgumentMatcherDiagnosticVerifier +{ + [CombinatoryTheory] + [MemberData(nameof(MisusedArgTestCasesWithoutDelegates))] + public abstract Task ReportsDiagnostics_WhenUsingInvalidArgMatchersWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg); + + [CombinatoryTheory] + [MemberData(nameof(CorrectlyUsedArgTestCasesWithoutDelegates))] + public abstract Task ReportsNoDiagnostics_WhenUsingArgMatchersWithPropertyNotCombinedWithAnyArgsLikeMethod(string method, string arg); + + [CombinatoryTheory] + [MemberData(nameof(ArgAnyTestCases))] + public abstract Task ReportsNoDiagnostics_WhenUsingArgAnyMatcherWithPropertyCombinedWithAnyArgsLikeMethod(string method, string arg); + + [CombinatoryTheory] + [MemberData(nameof(CorrectlyUsedArgTestCasesDelegates))] + public abstract Task ReportsNoDiagnostics_WhenAssigningArgMatchersToMemberNotPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg); + + [CombinatoryTheory] + [MemberData(nameof(MisusedArgTestCasesDelegates))] + public abstract Task ReportsDiagnostics_WhenAssigningInvalidArgMatchersToMemberPrecededByWithAnyArgsLikeMethodForDelegate(string method, string arg); +} \ No newline at end of file