diff --git a/src/xunit.analyzers.fixes/X2000/AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheck.cs b/src/xunit.analyzers.fixes/X2000/AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheckFixer.cs similarity index 76% rename from src/xunit.analyzers.fixes/X2000/AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheck.cs rename to src/xunit.analyzers.fixes/X2000/AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheckFixer.cs index a70927e2..dee4e1c9 100644 --- a/src/xunit.analyzers.fixes/X2000/AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheck.cs +++ b/src/xunit.analyzers.fixes/X2000/AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheckFixer.cs @@ -1,10 +1,10 @@ using System.Composition; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis; +using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; -using System.Threading; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; @@ -13,7 +13,7 @@ namespace Xunit.Analyzers.Fixes; [ExportCodeFixProvider(LanguageNames.CSharp), Shared] public class AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheckFixer : BatchedCodeFixProvider { - public const string Key_UseAlternateAssert = "xUnit2017_UseAlternateAssert"; + public const string Key_UseAlternateAssert = "xUnit2029_UseAlternateAssert"; public AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheckFixer() : base(Descriptors.X2029_AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheck.Id) @@ -30,9 +30,10 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) return; context.RegisterCodeFix( - XunitCodeAction.Create(c => UseCheck(context.Document, invocation, c), - Key_UseAlternateAssert, - "Use DoesNotContain" + XunitCodeAction.Create( + c => UseCheck(context.Document, invocation, c), + Key_UseAlternateAssert, + "Use DoesNotContain" ), context.Diagnostics ); @@ -47,32 +48,15 @@ static async Task UseCheck( var arguments = invocation.ArgumentList.Arguments; if (arguments.Count == 1 && arguments[0].Expression is InvocationExpressionSyntax innerInvocationSyntax) - { if (invocation.Expression is MemberAccessExpressionSyntax outerMemberAccess && innerInvocationSyntax.Expression is MemberAccessExpressionSyntax memberAccess) - { if (innerInvocationSyntax.ArgumentList.Arguments[0].Expression is ExpressionSyntax innerArgument) - { - editor.ReplaceNode(invocation, + editor.ReplaceNode( + invocation, invocation - .WithArgumentList( - ArgumentList( - SeparatedList(new[] { - Argument(memberAccess.Expression), - Argument(innerArgument) } - ) - ) - ) - .WithExpression( - outerMemberAccess.WithName( - IdentifierName(Constants.Asserts.DoesNotContain) - ) - ) + .WithArgumentList(ArgumentList(SeparatedList([Argument(memberAccess.Expression), Argument(innerArgument)]))) + .WithExpression(outerMemberAccess.WithName(IdentifierName(Constants.Asserts.DoesNotContain))) ); - } - } - } - return editor.GetChangedDocument(); } } diff --git a/src/xunit.analyzers.tests/Analyzers/X2000/AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheckTests.cs b/src/xunit.analyzers.tests/Analyzers/X2000/AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheckTests.cs index 0bce0dc1..d806fc0f 100644 --- a/src/xunit.analyzers.tests/Analyzers/X2000/AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheckTests.cs +++ b/src/xunit.analyzers.tests/Analyzers/X2000/AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheckTests.cs @@ -1,53 +1,45 @@ -using System; using System.Threading.Tasks; using Xunit; using Verify = CSharpVerifier; - - public class AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheckTests { - public static TheoryData GetEnumerables(string typeName) - { - return new TheoryData() - { - $"new System.Collections.Generic.List<{typeName}>()", - $"new System.Collections.Generic.HashSet<{typeName}>()", - $"new System.Collections.ObjectModel.Collection<{typeName}>()", - $"new {typeName}[0]", - $"System.Linq.Enumerable.Empty<{typeName}>()" - }; - } - - public static TheoryData GetSampleStrings() - { - return new TheoryData() - { - String.Empty, - "123", - @"abc\n\t\\\""" - }; - } + public static TheoryData GetEnumerables( + string typeName, + string comparison) => + new() + { + { $"new System.Collections.Generic.List<{typeName}>()", comparison }, + { $"new System.Collections.Generic.HashSet<{typeName}>()", comparison }, + { $"new System.Collections.ObjectModel.Collection<{typeName}>()", comparison }, + { $"new {typeName}[0]", comparison }, + { $"System.Linq.Enumerable.Empty<{typeName}>()", comparison }, + }; [Theory] - [MemberData(nameof(GetEnumerables), "int")] - public async Task FindsWarningForIntEnumerableDoesNotContainCheckWithEmpty(string collection) + [MemberData(nameof(GetEnumerables), "int", "")] + [MemberData(nameof(GetEnumerables), "string", "")] + public async Task Containers_WithoutWhereClause_DoesNotTrigger( + string collection, + string _) { var source = $@" -using System.Linq; class TestClass {{ void TestMethod() {{ - [|Xunit.Assert.Empty({collection}.Where(f => f > 0))|]; + Xunit.Assert.Empty({collection}); }} }}"; await Verify.VerifyAnalyzer(source); } [Theory] - [MemberData(nameof(GetEnumerables), "string")] - public async Task FindsWarningForStringEnumerableDoesNotContainCheckWithEmpty(string collection) + [MemberData(nameof(GetEnumerables), "int", "f > 0")] + [MemberData(nameof(GetEnumerables), "string", "f.Length > 0")] + public async Task Containers_WithWhereClause_Triggers( + string collection, + string comparison) { var source = $@" using System.Linq; @@ -55,31 +47,19 @@ class TestClass {{ void TestMethod() {{ - [|Xunit.Assert.Empty({collection}.Where(f => f.Length > 0))|]; + [|Xunit.Assert.Empty({collection}.Where(f => {comparison}))|]; }} }}"; - await Verify.VerifyAnalyzer(source); - } - [Theory] - [MemberData(nameof(GetSampleStrings))] - public async Task FindsWarningForStringDoesNotContainCheckWithEmpty(string sampleString) - { - var source = $@" -using System.Linq; -class TestClass -{{ - void TestMethod() - {{ - [|Xunit.Assert.Empty(""{sampleString}"".Where(f => f > 0))|]; - }} -}}"; await Verify.VerifyAnalyzer(source); } [Theory] - [MemberData(nameof(GetEnumerables), "int")] - public async Task DoesNotFindWarningForIntEnumerableDoesNotContainCheckWithEmptyWithIndex(string collection) + [MemberData(nameof(GetEnumerables), "int", "f > 0")] + [MemberData(nameof(GetEnumerables), "string", "f.Length > 0")] + public async Task Containers_WithWhereClauseWithIndex_DoesNotTrigger( + string collection, + string comparison) { var source = $@" using System.Linq; @@ -87,15 +67,18 @@ class TestClass {{ void TestMethod() {{ - Xunit.Assert.Empty({collection}.Where((f, i) => f > 0 && i > 0)); + Xunit.Assert.Empty({collection}.Where((f, i) => {comparison} && i > 0)); }} }}"; await Verify.VerifyAnalyzer(source); } [Theory] - [MemberData(nameof(GetEnumerables), "string")] - public async Task DoesNotFindWarningForStringEnumerableDoesNotContainCheckWithEmptyWithIndex(string collection) + [MemberData(nameof(GetEnumerables), "int", "f > 0")] + [MemberData(nameof(GetEnumerables), "string", "f.Length > 0")] + public async Task DoesNotFindWarningForEnumurableEmptyCheckWithChainedLinq( + string collection, + string comparison) { var source = $@" using System.Linq; @@ -103,42 +86,28 @@ class TestClass {{ void TestMethod() {{ - Xunit.Assert.Empty({collection}.Where((f, i) => f.Length > 0 && i > 0)); + Xunit.Assert.Empty({collection}.Where(f => {comparison}).Select(f => f)); }} }}"; await Verify.VerifyAnalyzer(source); } + public static TheoryData GetSampleStrings() => + new(string.Empty, "123", @"abc\n\t\\\"""); + [Theory] - [MemberData(nameof(GetEnumerables), "int")] - [MemberData(nameof(GetEnumerables), "string")] - public async Task DoesNotFindWarningForEnumerableEmptyCheckWithoutLinq(string collection) + [MemberData(nameof(GetSampleStrings))] + public async Task Strings_WithWhereClause_DoesNotTrigger(string sampleString) { var source = $@" +using System.Linq; class TestClass {{ void TestMethod() {{ - Xunit.Assert.Empty({collection}); + [|Xunit.Assert.Empty(""{sampleString}"".Where(f => f > 0))|]; }} }}"; await Verify.VerifyAnalyzer(source); } - - [Theory] - [MemberData(nameof(GetEnumerables), "int")] - [MemberData(nameof(GetEnumerables), "string")] - public async Task DoesNotFindWarningForEnumurableEmptyCheckWithChainedLinq(string collection) - { - var source = $@" -using System.Linq; -class TestClass -{{ - void TestMethod() - {{ - Xunit.Assert.Empty({collection}.Where(f => f.ToString().Length > 0).Select(f => f)); - }} -}}"; - await Verify.VerifyAnalyzer(source); - } } diff --git a/src/xunit.analyzers/X2000/AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheck.cs b/src/xunit.analyzers/X2000/AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheck.cs index c2ee67df..3526e020 100644 --- a/src/xunit.analyzers/X2000/AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheck.cs +++ b/src/xunit.analyzers/X2000/AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheck.cs @@ -1,7 +1,7 @@ -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; namespace Xunit.Analyzers; @@ -20,9 +20,9 @@ public AssertEmptyShouldNotBeUsedForCollectionDoesNotContainCheck() { } protected override void AnalyzeInvocation( - OperationAnalysisContext context, - XunitContext xunitContext, - IInvocationOperation invocationOperation, + OperationAnalysisContext context, + XunitContext xunitContext, + IInvocationOperation invocationOperation, IMethodSymbol method) { Guard.ArgumentNotNull(xunitContext);