From 8a4ee92f7a8e7a6ada770bcfafb06badcef86794 Mon Sep 17 00:00:00 2001 From: Manfred Brands Date: Tue, 14 Nov 2023 16:07:54 +0800 Subject: [PATCH] Allow IAsyncEnumerable for ValueSource/TestCaseSource --- documentation/NUnit1015.md | 20 +++++------ documentation/NUnit1019.md | 20 +++++------ documentation/NUnit1024.md | 20 +++++------ documentation/index.md | 6 ++-- .../TestCaseSourceSourceTypeTests.cs | 32 ++++++++++++++++- .../TestCaseSourceUsesStringAnalyzerTests.cs | 2 +- .../ValueSourceUsageAnalyzerTests.cs | 16 ++++++--- .../Extensions/ITypeSymbolExtensions.cs | 34 +++++++++++++++++++ .../TestCaseSourceUsageConstants.cs | 12 +++---- .../TestCaseSourceUsesStringAnalyzer.cs | 4 +-- .../ValueSourceUsageAnalyzer.cs | 2 +- .../ValueSourceUsageConstants.cs | 6 ++-- 12 files changed, 122 insertions(+), 52 deletions(-) diff --git a/documentation/NUnit1015.md b/documentation/NUnit1015.md index d843f768..1075e8c4 100644 --- a/documentation/NUnit1015.md +++ b/documentation/NUnit1015.md @@ -1,6 +1,6 @@ # NUnit1015 -## The source type does not implement IEnumerable +## The source type does not implement I(Async)Enumerable | Topic | Value | :-- | :-- @@ -12,7 +12,7 @@ ## Description -The source type must implement IEnumerable in order to provide test cases. +The source type must implement I(Async)Enumerable in order to provide test cases. ## Motivation @@ -45,13 +45,13 @@ class DivideCases ### Explanation -In the sample above, the class `DivideCases` does not implement `IEnumerable`. +In the sample above, the class `DivideCases` does not implement `IEnumerable` nor `IAsyncEnumerable` -However, source types specified by `TestCaseSource` [must implement `IEnumerable`](https://github.com/nunit/docs/wiki/TestCaseSource-Attribute). +However, source types specified by `TestCaseSource` [must implement `IEnumerable` or `IAsyncEnumerable`](https://github.com/nunit/docs/wiki/TestCaseSource-Attribute). ### Fix -Make the source type implement `IEnumerable`: +Make the source type implement `IEnumerable` or `IAsyncEnumerable` ```csharp public class MyTestClass @@ -84,7 +84,7 @@ Configure the severity per project, for more info see [MSDN](https://learn.micro ### Via .editorconfig file ```ini -# NUnit1015: The source type does not implement IEnumerable +# NUnit1015: The source type does not implement I(Async)Enumerable dotnet_diagnostic.NUnit1015.severity = chosenSeverity ``` @@ -93,22 +93,22 @@ where `chosenSeverity` can be one of `none`, `silent`, `suggestion`, `warning`, ### Via #pragma directive ```csharp -#pragma warning disable NUnit1015 // The source type does not implement IEnumerable +#pragma warning disable NUnit1015 // The source type does not implement I(Async)Enumerable Code violating the rule here -#pragma warning restore NUnit1015 // The source type does not implement IEnumerable +#pragma warning restore NUnit1015 // The source type does not implement I(Async)Enumerable ``` Or put this at the top of the file to disable all instances. ```csharp -#pragma warning disable NUnit1015 // The source type does not implement IEnumerable +#pragma warning disable NUnit1015 // The source type does not implement I(Async)Enumerable ``` ### Via attribute `[SuppressMessage]` ```csharp [System.Diagnostics.CodeAnalysis.SuppressMessage("Structure", - "NUnit1015:The source type does not implement IEnumerable", + "NUnit1015:The source type does not implement I(Async)Enumerable", Justification = "Reason...")] ``` diff --git a/documentation/NUnit1019.md b/documentation/NUnit1019.md index 419de3bf..5ef9bbd0 100644 --- a/documentation/NUnit1019.md +++ b/documentation/NUnit1019.md @@ -1,6 +1,6 @@ # NUnit1019 -## The source specified by the TestCaseSource does not return an IEnumerable or a type that implements IEnumerable +## The source specified by the TestCaseSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable | Topic | Value | :-- | :-- @@ -12,7 +12,7 @@ ## Description -The source specified by the TestCaseSource must return an IEnumerable or a type that implements IEnumerable. +The source specified by the TestCaseSource must return an I(Async)Enumerable or a type that implements I(Async)Enumerable. ## Motivation @@ -36,14 +36,14 @@ public class AnalyzeWhenSourceDoesProvideIEnumerable ### Explanation -In the sample above, the source specified by `TestCaseSource` - the field `testCases` - does not return an `IEnumerable` or a type that implements `IEnumerable`, +In the sample above, the source specified by `TestCaseSource` - the field `testCases` - does not return an `I(Async)Enumerable` or a type that implements `I(Async)Enumerable`, instead it returns an `int`. -However, sources specified by `TestCaseSource` [must return an `IEnumerable` or a type that implements `IEnumerable`.](https://github.com/nunit/docs/wiki/TestCaseSource-Attribute). +However, sources specified by `TestCaseSource` [must return an `I(Async)Enumerable` or a type that implements `I(Async)Enumerable`.](https://github.com/nunit/docs/wiki/TestCaseSource-Attribute). ### Fix -Change `testCases` to return an `IEnumerable` or a type that implements `IEnumerable`: +Change `testCases` to return an `I(Async)Enumerable` or a type that implements `I(Async)Enumerable`: ```csharp public class AnalyzeWhenSourceDoesProvideIEnumerable @@ -67,7 +67,7 @@ Configure the severity per project, for more info see [MSDN](https://learn.micro ### Via .editorconfig file ```ini -# NUnit1019: The source specified by the TestCaseSource does not return an IEnumerable or a type that implements IEnumerable +# NUnit1019: The source specified by the TestCaseSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable dotnet_diagnostic.NUnit1019.severity = chosenSeverity ``` @@ -76,22 +76,22 @@ where `chosenSeverity` can be one of `none`, `silent`, `suggestion`, `warning`, ### Via #pragma directive ```csharp -#pragma warning disable NUnit1019 // The source specified by the TestCaseSource does not return an IEnumerable or a type that implements IEnumerable +#pragma warning disable NUnit1019 // The source specified by the TestCaseSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable Code violating the rule here -#pragma warning restore NUnit1019 // The source specified by the TestCaseSource does not return an IEnumerable or a type that implements IEnumerable +#pragma warning restore NUnit1019 // The source specified by the TestCaseSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable ``` Or put this at the top of the file to disable all instances. ```csharp -#pragma warning disable NUnit1019 // The source specified by the TestCaseSource does not return an IEnumerable or a type that implements IEnumerable +#pragma warning disable NUnit1019 // The source specified by the TestCaseSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable ``` ### Via attribute `[SuppressMessage]` ```csharp [System.Diagnostics.CodeAnalysis.SuppressMessage("Structure", - "NUnit1019:The source specified by the TestCaseSource does not return an IEnumerable or a type that implements IEnumerable", + "NUnit1019:The source specified by the TestCaseSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable", Justification = "Reason...")] ``` diff --git a/documentation/NUnit1024.md b/documentation/NUnit1024.md index 446edd6c..31236116 100644 --- a/documentation/NUnit1024.md +++ b/documentation/NUnit1024.md @@ -1,6 +1,6 @@ # NUnit1024 -## The source specified by the ValueSource does not return an IEnumerable or a type that implements IEnumerable +## The source specified by the ValueSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable | Topic | Value | :-- | :-- @@ -12,7 +12,7 @@ ## Description -The source specified by the ValueSource must return an IEnumerable or a type that implements IEnumerable. +The source specified by the ValueSource must return an I(Async)Enumerable or a type that implements I(Async)Enumerable. ## Motivation @@ -36,14 +36,14 @@ public class AnalyzeWhenSourceDoesProvideIEnumerable ### Explanation -In the sample above, the source specified by `ValueSource` - the field `testCases` - does not return an `IEnumerable` or a type that implements `IEnumerable`, +In the sample above, the source specified by `ValueSource` - the field `testCases` - does not return an `I(Async)Enumerable` or a type that implements `I(Async)Enumerable`, instead it returns an `int`. -However, sources specified by `ValueSource` [must return an `IEnumerable` or a type that implements `IEnumerable`.](https://github.com/nunit/docs/wiki/ValueSource-Attribute). +However, sources specified by `ValueSource` [must return an `I(Async)Enumerable` or a type that implements `I(Async)Enumerable`.](https://github.com/nunit/docs/wiki/ValueSource-Attribute). ### Fix -Change `testCases` to return an `IEnumerable` or a type that implements `IEnumerable`: +Change `testCases` to return an `I(Async)Enumerable` or a type that implements `I(Async)Enumerable`: ```csharp public class AnalyzeWhenSourceDoesProvideIEnumerable @@ -67,7 +67,7 @@ Configure the severity per project, for more info see [MSDN](https://learn.micro ### Via .editorconfig file ```ini -# NUnit1024: The source specified by the ValueSource does not return an IEnumerable or a type that implements IEnumerable +# NUnit1024: The source specified by the ValueSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable dotnet_diagnostic.NUnit1024.severity = chosenSeverity ``` @@ -76,22 +76,22 @@ where `chosenSeverity` can be one of `none`, `silent`, `suggestion`, `warning`, ### Via #pragma directive ```csharp -#pragma warning disable NUnit1024 // The source specified by the ValueSource does not return an IEnumerable or a type that implements IEnumerable +#pragma warning disable NUnit1024 // The source specified by the ValueSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable Code violating the rule here -#pragma warning restore NUnit1024 // The source specified by the ValueSource does not return an IEnumerable or a type that implements IEnumerable +#pragma warning restore NUnit1024 // The source specified by the ValueSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable ``` Or put this at the top of the file to disable all instances. ```csharp -#pragma warning disable NUnit1024 // The source specified by the ValueSource does not return an IEnumerable or a type that implements IEnumerable +#pragma warning disable NUnit1024 // The source specified by the ValueSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable ``` ### Via attribute `[SuppressMessage]` ```csharp [System.Diagnostics.CodeAnalysis.SuppressMessage("Structure", - "NUnit1024:The source specified by the ValueSource does not return an IEnumerable or a type that implements IEnumerable", + "NUnit1024:The source specified by the ValueSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable", Justification = "Reason...")] ``` diff --git a/documentation/index.md b/documentation/index.md index 29f975c2..86fffff1 100644 --- a/documentation/index.md +++ b/documentation/index.md @@ -33,16 +33,16 @@ Rules which enforce structural requirements on the test code. | [NUnit1012](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1012.md) | The async test method must have a non-void return type | :white_check_mark: | :exclamation: | :x: | | [NUnit1013](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1013.md) | The async test method must have a non-generic Task return type when no result is expected | :white_check_mark: | :exclamation: | :x: | | [NUnit1014](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1014.md) | The async test method must have a Task\ return type when a result is expected | :white_check_mark: | :exclamation: | :x: | -| [NUnit1015](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1015.md) | The source type does not implement IEnumerable | :white_check_mark: | :exclamation: | :x: | +| [NUnit1015](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1015.md) | The source type does not implement I(Async)Enumerable | :white_check_mark: | :exclamation: | :x: | | [NUnit1016](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1016.md) | The source type does not have a default constructor | :white_check_mark: | :exclamation: | :x: | | [NUnit1017](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1017.md) | The specified source is not static | :white_check_mark: | :exclamation: | :x: | | [NUnit1018](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1018.md) | The number of parameters provided by the TestCaseSource does not match the number of parameters in the target method | :white_check_mark: | :exclamation: | :x: | -| [NUnit1019](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1019.md) | The source specified by the TestCaseSource does not return an IEnumerable or a type that implements IEnumerable | :white_check_mark: | :exclamation: | :x: | +| [NUnit1019](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1019.md) | The source specified by the TestCaseSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable | :white_check_mark: | :exclamation: | :x: | | [NUnit1020](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1020.md) | The TestCaseSource provides parameters to a source - field or property - that expects no parameters | :white_check_mark: | :exclamation: | :x: | | [NUnit1021](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1021.md) | The ValueSource should use nameof operator to specify target | :white_check_mark: | :warning: | :white_check_mark: | | [NUnit1022](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1022.md) | The specified source is not static | :white_check_mark: | :exclamation: | :x: | | [NUnit1023](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1023.md) | The target method expects parameters which cannot be supplied by the ValueSource | :white_check_mark: | :exclamation: | :x: | -| [NUnit1024](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1024.md) | The source specified by the ValueSource does not return an IEnumerable or a type that implements IEnumerable | :white_check_mark: | :exclamation: | :x: | +| [NUnit1024](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1024.md) | The source specified by the ValueSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable | :white_check_mark: | :exclamation: | :x: | | [NUnit1025](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1025.md) | The ValueSource argument does not specify an existing member | :white_check_mark: | :exclamation: | :x: | | [NUnit1026](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1026.md) | The test or setup/teardown method is not public | :white_check_mark: | :exclamation: | :white_check_mark: | | [NUnit1027](https://github.com/nunit/nunit.analyzers/tree/master/documentation/NUnit1027.md) | The test method has parameters, but no arguments are supplied by attributes | :white_check_mark: | :exclamation: | :x: | diff --git a/src/nunit.analyzers.tests/TestCaseSourceUsage/TestCaseSourceSourceTypeTests.cs b/src/nunit.analyzers.tests/TestCaseSourceUsage/TestCaseSourceSourceTypeTests.cs index 6ae6e74f..84ab5a2a 100644 --- a/src/nunit.analyzers.tests/TestCaseSourceUsage/TestCaseSourceSourceTypeTests.cs +++ b/src/nunit.analyzers.tests/TestCaseSourceUsage/TestCaseSourceSourceTypeTests.cs @@ -1,4 +1,7 @@ +using System.Collections.Generic; +using System.Linq; using Gu.Roslyn.Asserts; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Analyzers.Constants; using NUnit.Analyzers.TestCaseSourceUsage; @@ -51,10 +54,37 @@ class MyTests var expectedDiagnostic = ExpectedDiagnostic .Create(AnalyzerIdentifiers.TestCaseSourceSourceTypeNotIEnumerable) - .WithMessage("The source type 'MyTests' does not implement IEnumerable"); + .WithMessage("The source type 'MyTests' does not implement I(Async)Enumerable"); RoslynAssert.Diagnostics(analyzer, expectedDiagnostic, testCode); } + [Test] + public void AnalyzeWhenTypeOfSourceImplementsIAsyncEnumerable() + { + var testCode = TestUtility.WrapClassInNamespaceAndAddUsing(@" + public class AnalyzeWhenTypeOfSourceImplementsIAsyncEnumerable + { + [TestCaseSource(typeof(MyTests))] + public void Test(int i) + { + } + } + + public sealed class MyTests : IAsyncEnumerable + { + public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default(CancellationToken)) + { + throw new NotImplementedException(); + } + }", + additionalUsings: "using System.Collections.Generic;using System.Threading;"); + + IEnumerable asyncEnumerableMetadata = MetadataReferences.Transitive(typeof(IAsyncEnumerable<>)); + IEnumerable metadataReferences = (Settings.Default.MetadataReferences ?? Enumerable.Empty()).Concat(asyncEnumerableMetadata); + + RoslynAssert.Valid(analyzer, testCode, Settings.Default.WithMetadataReferences(metadataReferences)); + } + [Test] public void AnalyzeWhenTypeOfSourceNoDefaultConstructor() { diff --git a/src/nunit.analyzers.tests/TestCaseSourceUsage/TestCaseSourceUsesStringAnalyzerTests.cs b/src/nunit.analyzers.tests/TestCaseSourceUsage/TestCaseSourceUsesStringAnalyzerTests.cs index 0d9d7fab..b046e8d0 100644 --- a/src/nunit.analyzers.tests/TestCaseSourceUsage/TestCaseSourceUsesStringAnalyzerTests.cs +++ b/src/nunit.analyzers.tests/TestCaseSourceUsage/TestCaseSourceUsesStringAnalyzerTests.cs @@ -322,7 +322,7 @@ public void Test() var expectedDiagnostic = ExpectedDiagnostic .Create(AnalyzerIdentifiers.TestCaseSourceDoesNotReturnIEnumerable) - .WithMessage($"The TestCaseSource does not return an IEnumerable or a type that implements IEnumerable. Instead it returns a '{returnType}'."); + .WithMessage($"The TestCaseSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable. Instead it returns a '{returnType}'."); RoslynAssert.Diagnostics(analyzer, expectedDiagnostic, testCode); } diff --git a/src/nunit.analyzers.tests/ValueSourceUsage/ValueSourceUsageAnalyzerTests.cs b/src/nunit.analyzers.tests/ValueSourceUsage/ValueSourceUsageAnalyzerTests.cs index e32209ad..c37573d6 100644 --- a/src/nunit.analyzers.tests/ValueSourceUsage/ValueSourceUsageAnalyzerTests.cs +++ b/src/nunit.analyzers.tests/ValueSourceUsage/ValueSourceUsageAnalyzerTests.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; +using System.Linq; using Gu.Roslyn.Asserts; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; @@ -199,7 +201,8 @@ public static IEnumerable TestData(string dummy, int anotherDummy) [TestCase("private static TestCaseData[] TestCases => new TestCaseData[0];")] [TestCase("private static TestCaseData[] TestCases() => new TestCaseData[0];")] [TestCase("private static Task TestCases() => Task.FromResult(new TestCaseData[0]);")] - public void AnalyzeWhenSourceDoesProvideIEnumerable(string testCaseMember) + [TestCase("private static async IAsyncEnumerable TestCases() { foreach (var value in new[] { 0 }) { yield return value; await Task.Yield(); } }", "using System.Collections.Generic;")] + public void AnalyzeWhenSourceDoesProvideIEnumerable(string testCaseMember, string? additionalUsings = null) { var testCode = TestUtility.WrapClassInNamespaceAndAddUsing($@" public class AnalyzeWhenSourceDoesProvideIEnumerable @@ -210,9 +213,12 @@ public class AnalyzeWhenSourceDoesProvideIEnumerable public void Test([ValueSource(nameof(TestCases))] int number) {{ }} - }}"); + }}", additionalUsings); - RoslynAssert.Valid(analyzer, testCode); + IEnumerable asyncEnumerableMetadata = MetadataReferences.Transitive(typeof(IAsyncEnumerable<>)); + IEnumerable metadataReferences = (Settings.Default.MetadataReferences ?? Enumerable.Empty()).Concat(asyncEnumerableMetadata); + + RoslynAssert.Valid(analyzer, testCode, Settings.Default.WithMetadataReferences(metadataReferences)); } [TestCase("private static readonly object? TestCases = null;", "object?")] @@ -230,7 +236,7 @@ public void Test([ValueSource(nameof(TestCases))] int number) public void AnalyzeWhenSourceDoesNotProvideIEnumerable(string testCaseMember, string returnType) { var testCode = TestUtility.WrapClassInNamespaceAndAddUsing($@" - public class AnalyzeWhenSourceDoesProvideIEnumerable + public class AnalyzeWhenSourceDoesNotProvideIEnumerable {{ #pragma warning disable CS0414 // Consider changing the field to a 'const' {testCaseMember} @@ -244,7 +250,7 @@ public void Test([ValueSource(nameof(TestCases))] int number) var expectedDiagnostic = ExpectedDiagnostic .Create(AnalyzerIdentifiers.ValueSourceDoesNotReturnIEnumerable) - .WithMessage($"The ValueSource does not return an IEnumerable or a type that implements IEnumerable. Instead it returns a '{returnType}'."); + .WithMessage($"The ValueSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable. Instead it returns a '{returnType}'."); RoslynAssert.Diagnostics(analyzer, expectedDiagnostic, testCode); } diff --git a/src/nunit.analyzers/Extensions/ITypeSymbolExtensions.cs b/src/nunit.analyzers/Extensions/ITypeSymbolExtensions.cs index 7add9c61..26587ec6 100644 --- a/src/nunit.analyzers/Extensions/ITypeSymbolExtensions.cs +++ b/src/nunit.analyzers/Extensions/ITypeSymbolExtensions.cs @@ -209,6 +209,40 @@ internal static bool IsIEnumerable(this ITypeSymbol @this, out ITypeSymbol? elem return false; } + /// + /// Return value indicates whether type implements I(Async)Enumerable{T} interface. + /// + /// Contains I(Async)Enumerable generic argument, or null, if type implements + /// only non-generic IEnumerable interface, or no I(Async)Enumerable interface at all. + internal static bool IsIEnumerableOrIAsyncEnumerable(this ITypeSymbol @this, out ITypeSymbol? elementType) + { + elementType = null; + + var allInterfaces = @this.AllInterfaces; + + if (@this is INamedTypeSymbol namedType && namedType.TypeKind == TypeKind.Interface) + allInterfaces = allInterfaces.Add(namedType); + + var genericIEnumerableInterface = allInterfaces.FirstOrDefault(i => + i.GetFullMetadataName() is "System.Collections.Generic.IEnumerable`1" or "System.Collections.Generic.IAsyncEnumerable`1"); + + if (genericIEnumerableInterface is not null) + { + elementType = genericIEnumerableInterface.TypeArguments.FirstOrDefault(); + return true; + } + + var nonGenericIEnumerableInterface = allInterfaces.FirstOrDefault(i => + i.GetFullMetadataName() == "System.Collections.IEnumerable"); + + if (nonGenericIEnumerableInterface is not null) + { + return true; + } + + return false; + } + /// /// Return value indicates whether type implements IDisposable. /// diff --git a/src/nunit.analyzers/TestCaseSourceUsage/TestCaseSourceUsageConstants.cs b/src/nunit.analyzers/TestCaseSourceUsage/TestCaseSourceUsageConstants.cs index 0d2416c9..69663add 100644 --- a/src/nunit.analyzers/TestCaseSourceUsage/TestCaseSourceUsageConstants.cs +++ b/src/nunit.analyzers/TestCaseSourceUsage/TestCaseSourceUsageConstants.cs @@ -6,9 +6,9 @@ internal static class TestCaseSourceUsageConstants internal const string ConsiderNameOfInsteadOfStringConstantMessage = "Consider using nameof({0}) instead of \"{1}\""; internal const string ConsiderNameOfInsteadOfStringConstantDescription = "The TestCaseSource should use nameof operator to specify target."; - internal const string SourceTypeNotIEnumerableTitle = "The source type does not implement IEnumerable"; - internal const string SourceTypeNotIEnumerableMessage = "The source type '{0}' does not implement IEnumerable"; - internal const string SourceTypeNotIEnumerableDescription = "The source type must implement IEnumerable in order to provide test cases."; + internal const string SourceTypeNotIEnumerableTitle = "The source type does not implement I(Async)Enumerable"; + internal const string SourceTypeNotIEnumerableMessage = "The source type '{0}' does not implement I(Async)Enumerable"; + internal const string SourceTypeNotIEnumerableDescription = "The source type must implement I(Async)Enumerable in order to provide test cases."; internal const string SourceTypeNoDefaultConstructorTitle = "The source type does not have a default constructor"; internal const string SourceTypeNoDefaultConstructorMessage = "The source type '{0}' does not have a default constructor"; @@ -22,9 +22,9 @@ internal static class TestCaseSourceUsageConstants internal const string MismatchInNumberOfParametersMessage = "The TestCaseSource provides '{0}' parameter(s), but the target method expects '{1}' parameter(s)"; internal const string MismatchInNumberOfParametersDescription = "The number of parameters provided by the TestCaseSource must match the number of parameters in the target method."; - internal const string SourceDoesNotReturnIEnumerableTitle = "The source specified by the TestCaseSource does not return an IEnumerable or a type that implements IEnumerable"; - internal const string SourceDoesNotReturnIEnumerableMessage = "The TestCaseSource does not return an IEnumerable or a type that implements IEnumerable. Instead it returns a '{0}'."; - internal const string SourceDoesNotReturnIEnumerableDescription = "The source specified by the TestCaseSource must return an IEnumerable or a type that implements IEnumerable."; + internal const string SourceDoesNotReturnIEnumerableTitle = "The source specified by the TestCaseSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable"; + internal const string SourceDoesNotReturnIEnumerableMessage = "The TestCaseSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable. Instead it returns a '{0}'."; + internal const string SourceDoesNotReturnIEnumerableDescription = "The source specified by the TestCaseSource must return an I(Async)Enumerable or a type that implements I(Async)Enumerable."; internal const string TestCaseSourceSuppliesParametersTitle = "The TestCaseSource provides parameters to a source - field or property - that expects no parameters"; internal const string TestCaseSourceSuppliesParametersMessage = "The TestCaseSource provides '{0}' parameter(s), but {1} cannot take parameters"; diff --git a/src/nunit.analyzers/TestCaseSourceUsage/TestCaseSourceUsesStringAnalyzer.cs b/src/nunit.analyzers/TestCaseSourceUsage/TestCaseSourceUsesStringAnalyzer.cs index 5e3fa485..6a0b554d 100644 --- a/src/nunit.analyzers/TestCaseSourceUsage/TestCaseSourceUsesStringAnalyzer.cs +++ b/src/nunit.analyzers/TestCaseSourceUsage/TestCaseSourceUsesStringAnalyzer.cs @@ -144,7 +144,7 @@ private static void AnalyzeAttribute(SyntaxNodeAnalysisContext context, INamedTy // The Type argument in this form represents the class that provides test cases. // It must have a default constructor and implement IEnumerable. var sourceType = attributeInfo.SourceType; - bool typeImplementsIEnumerable = sourceType.IsIEnumerable(out _); + bool typeImplementsIEnumerable = sourceType.IsIEnumerableOrIAsyncEnumerable(out _); bool typeHasDefaultConstructor = sourceType.Constructors.Any(c => c.Parameters.IsEmpty); if (!typeImplementsIEnumerable) { @@ -292,7 +292,7 @@ private static void AnalyzeAttribute(SyntaxNodeAnalysisContext context, INamedTy SyntaxNode syntaxNode, ITypeSymbol typeSymbol) { - if (!typeSymbol.IsIEnumerable(out ITypeSymbol? elementType)) + if (!typeSymbol.IsIEnumerableOrIAsyncEnumerable(out ITypeSymbol? elementType)) { context.ReportDiagnostic(Diagnostic.Create( sourceDoesNotReturnIEnumerable, diff --git a/src/nunit.analyzers/ValueSourceUsage/ValueSourceUsageAnalyzer.cs b/src/nunit.analyzers/ValueSourceUsage/ValueSourceUsageAnalyzer.cs index 8ead1fd0..a95c31f7 100644 --- a/src/nunit.analyzers/ValueSourceUsage/ValueSourceUsageAnalyzer.cs +++ b/src/nunit.analyzers/ValueSourceUsage/ValueSourceUsageAnalyzer.cs @@ -152,7 +152,7 @@ private static void AnalyzeAttribute(SyntaxNodeAnalysisContext context, INamedTy if (symbol is IMethodSymbol && memberType.IsAwaitable(out ITypeSymbol? returnType)) memberType = returnType; - if (!memberType.IsIEnumerable(out var _)) + if (!memberType.IsIEnumerableOrIAsyncEnumerable(out var _)) { context.ReportDiagnostic(Diagnostic.Create( sourceDoesNotReturnIEnumerable, diff --git a/src/nunit.analyzers/ValueSourceUsage/ValueSourceUsageConstants.cs b/src/nunit.analyzers/ValueSourceUsage/ValueSourceUsageConstants.cs index 34f506a7..01747723 100644 --- a/src/nunit.analyzers/ValueSourceUsage/ValueSourceUsageConstants.cs +++ b/src/nunit.analyzers/ValueSourceUsage/ValueSourceUsageConstants.cs @@ -18,8 +18,8 @@ internal static class ValueSourceUsageConstants internal const string MethodExpectParametersMessage = "The ValueSource cannot supply parameters, but the target method expects '{0}' parameter(s)"; internal const string MethodExpectParametersDescription = "The target method expects parameters which cannot be supplied by the ValueSource."; - internal const string SourceDoesNotReturnIEnumerableTitle = "The source specified by the ValueSource does not return an IEnumerable or a type that implements IEnumerable"; - internal const string SourceDoesNotReturnIEnumerableMessage = "The ValueSource does not return an IEnumerable or a type that implements IEnumerable. Instead it returns a '{0}'."; - internal const string SourceDoesNotReturnIEnumerableDescription = "The source specified by the ValueSource must return an IEnumerable or a type that implements IEnumerable."; + internal const string SourceDoesNotReturnIEnumerableTitle = "The source specified by the ValueSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable"; + internal const string SourceDoesNotReturnIEnumerableMessage = "The ValueSource does not return an I(Async)Enumerable or a type that implements I(Async)Enumerable. Instead it returns a '{0}'."; + internal const string SourceDoesNotReturnIEnumerableDescription = "The source specified by the ValueSource must return an I(Async)Enumerable or a type that implements I(Async)Enumerable."; } }