diff --git a/src/xunit.analyzers.fixes/AssertCollectionContainsShouldNotUseBoolCheckFixer.cs b/src/xunit.analyzers.fixes/AssertCollectionContainsShouldNotUseBoolCheckFixer.cs index e68094bd..d7e6506b 100644 --- a/src/xunit.analyzers.fixes/AssertCollectionContainsShouldNotUseBoolCheckFixer.cs +++ b/src/xunit.analyzers.fixes/AssertCollectionContainsShouldNotUseBoolCheckFixer.cs @@ -28,8 +28,8 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); var diagnostic = context.Diagnostics.First(); - var assertMethodName = diagnostic.Properties[Constants.Properties.MethodName]; - var replacement = assertMethodName == Constants.Asserts.True ? Constants.Asserts.Contains : Constants.Asserts.DoesNotContain; + var methodName = diagnostic.Properties[Constants.Properties.MethodName]; + var replacement = diagnostic.Properties[Constants.Properties.Replacement]; var title = string.Format(titleTemplate, replacement); context.RegisterCodeFix( diff --git a/src/xunit.analyzers.fixes/AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheckFixer.cs b/src/xunit.analyzers.fixes/AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheckFixer.cs index d569c466..260d88a8 100644 --- a/src/xunit.analyzers.fixes/AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheckFixer.cs +++ b/src/xunit.analyzers.fixes/AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheckFixer.cs @@ -29,7 +29,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); var diagnostic = context.Diagnostics.First(); var assertMethodName = diagnostic.Properties[Constants.Properties.AssertMethodName]; - var replacement = assertMethodName == Constants.Asserts.True ? Constants.Asserts.Contains : Constants.Asserts.DoesNotContain; + var replacement = diagnostic.Properties[Constants.Properties.Replacement]; var title = string.Format(titleTemplate, replacement); context.RegisterCodeFix( diff --git a/src/xunit.analyzers.fixes/AssertEqualShouldNotBeUsedForBoolLiteralCheckFixer.cs b/src/xunit.analyzers.fixes/AssertEqualShouldNotBeUsedForBoolLiteralCheckFixer.cs index 24dca6c1..19c23d01 100644 --- a/src/xunit.analyzers.fixes/AssertEqualShouldNotBeUsedForBoolLiteralCheckFixer.cs +++ b/src/xunit.analyzers.fixes/AssertEqualShouldNotBeUsedForBoolLiteralCheckFixer.cs @@ -30,7 +30,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var diagnostic = context.Diagnostics.First(); var methodName = diagnostic.Properties[Constants.Properties.MethodName]; var literalValue = diagnostic.Properties[Constants.Properties.LiteralValue]; - var replacement = GetReplacementMethodName(methodName, literalValue); + var replacement = diagnostic.Properties[Constants.Properties.Replacement]; if (replacement != null && invocation.Expression is MemberAccessExpressionSyntax) { @@ -46,18 +46,6 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) } } - static string GetReplacementMethodName( - string methodName, - string literalValue) - { - if (AssertEqualShouldNotBeUsedForBoolLiteralCheck.EqualMethods.Contains(methodName)) - return literalValue == bool.TrueString ? Constants.Asserts.True : Constants.Asserts.False; - if (AssertEqualShouldNotBeUsedForBoolLiteralCheck.NotEqualMethods.Contains(methodName)) - return literalValue == bool.TrueString ? Constants.Asserts.False : Constants.Asserts.True; - - return null; - } - static async Task UseBoolCheckAsync( Document document, InvocationExpressionSyntax invocation, diff --git a/src/xunit.analyzers.fixes/AssertEqualShouldNotBeUsedForCollectionSizeCheckFixer.cs b/src/xunit.analyzers.fixes/AssertEqualShouldNotBeUsedForCollectionSizeCheckFixer.cs index abf24324..153bc9f3 100644 --- a/src/xunit.analyzers.fixes/AssertEqualShouldNotBeUsedForCollectionSizeCheckFixer.cs +++ b/src/xunit.analyzers.fixes/AssertEqualShouldNotBeUsedForCollectionSizeCheckFixer.cs @@ -30,7 +30,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var diagnostic = context.Diagnostics.First(); var methodName = diagnostic.Properties[Constants.Properties.MethodName]; var sizeValue = diagnostic.Properties[Constants.Properties.SizeValue]; - var replacement = GetReplacementMethodName(methodName, sizeValue); + var replacement = diagnostic.Properties[Constants.Properties.Replacement]; var title = string.Format(titleTemplate, replacement); context.RegisterCodeFix( @@ -43,16 +43,6 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) ); } - static string GetReplacementMethodName( - string methodName, - string literalValue) - { - if (literalValue == "1") - return Constants.Asserts.Single; - - return methodName == Constants.Asserts.Equal ? Constants.Asserts.Empty : Constants.Asserts.NotEmpty; - } - static async Task UseCollectionSizeAssertionAsync( Document document, InvocationExpressionSyntax invocation, diff --git a/src/xunit.analyzers.fixes/AssertEqualShouldNotBeUsedForNullCheckFixer.cs b/src/xunit.analyzers.fixes/AssertEqualShouldNotBeUsedForNullCheckFixer.cs index b075e47f..ceac1b21 100644 --- a/src/xunit.analyzers.fixes/AssertEqualShouldNotBeUsedForNullCheckFixer.cs +++ b/src/xunit.analyzers.fixes/AssertEqualShouldNotBeUsedForNullCheckFixer.cs @@ -29,7 +29,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); var diagnostic = context.Diagnostics.First(); var methodName = diagnostic.Properties[Constants.Properties.MethodName]; - var replacement = GetReplacementMethod(methodName); + var replacement = diagnostic.Properties[Constants.Properties.Replacement]; if (replacement != null && invocation.Expression is MemberAccessExpressionSyntax) { @@ -45,16 +45,6 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) } } - static string GetReplacementMethod(string methodName) - { - if (AssertEqualShouldNotBeUsedForNullCheck.EqualMethods.Contains(methodName)) - return Constants.Asserts.Null; - if (AssertEqualShouldNotBeUsedForNullCheck.NotEqualMethods.Contains(methodName)) - return Constants.Asserts.NotNull; - - return null; - } - static async Task UseNullCheckAsync( Document document, InvocationExpressionSyntax invocation, diff --git a/src/xunit.analyzers.fixes/AssertEqualsShouldNotBeUsedFixer.cs b/src/xunit.analyzers.fixes/AssertEqualsShouldNotBeUsedFixer.cs index 8501c831..3eb63a11 100644 --- a/src/xunit.analyzers.fixes/AssertEqualsShouldNotBeUsedFixer.cs +++ b/src/xunit.analyzers.fixes/AssertEqualsShouldNotBeUsedFixer.cs @@ -25,12 +25,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); var diagnostic = context.Diagnostics.First(); - var replacement = diagnostic.Properties[Constants.Properties.MethodName] switch - { - nameof(object.Equals) => Constants.Asserts.Equal, - nameof(object.ReferenceEquals) => Constants.Asserts.Same, - _ => null, - }; + var replacement = diagnostic.Properties[Constants.Properties.Replacement]; if (replacement != null && invocation.Expression is MemberAccessExpressionSyntax) { diff --git a/src/xunit.analyzers.fixes/AssertRegexMatchShouldNotUseBoolLiteralCheckFixer.cs b/src/xunit.analyzers.fixes/AssertRegexMatchShouldNotUseBoolLiteralCheckFixer.cs index 7e50c33e..e9b1473a 100644 --- a/src/xunit.analyzers.fixes/AssertRegexMatchShouldNotUseBoolLiteralCheckFixer.cs +++ b/src/xunit.analyzers.fixes/AssertRegexMatchShouldNotUseBoolLiteralCheckFixer.cs @@ -30,7 +30,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var diagnostic = context.Diagnostics.First(); var methodName = diagnostic.Properties[Constants.Properties.MethodName]; var isStatic = diagnostic.Properties[Constants.Properties.IsStatic]; - var replacementMethod = methodName == Constants.Asserts.True ? Constants.Asserts.Matches : Constants.Asserts.DoesNotMatch; + var replacementMethod = diagnostic.Properties[Constants.Properties.Replacement]; var title = string.Format(titleTemplate, replacementMethod); context.RegisterCodeFix( diff --git a/src/xunit.analyzers.fixes/AssertSameShouldNotBeCalledOnValueTypesFixer.cs b/src/xunit.analyzers.fixes/AssertSameShouldNotBeCalledOnValueTypesFixer.cs index 05972dc6..51c95590 100644 --- a/src/xunit.analyzers.fixes/AssertSameShouldNotBeCalledOnValueTypesFixer.cs +++ b/src/xunit.analyzers.fixes/AssertSameShouldNotBeCalledOnValueTypesFixer.cs @@ -23,14 +23,10 @@ public sealed override FixAllProvider GetFixAllProvider() => public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); - var methodName = context.Diagnostics.First().Properties[Constants.Properties.MethodName]; + var diagnostic = context.Diagnostics.First(); + var methodName = diagnostic.Properties[Constants.Properties.MethodName]; var invocation = root.FindNode(context.Span).FirstAncestorOrSelf(); - var replacement = methodName switch - { - Constants.Asserts.Same => Constants.Asserts.Equal, - Constants.Asserts.NotSame => Constants.Asserts.NotEqual, - _ => null, - }; + var replacement = diagnostic.Properties[Constants.Properties.Replacement]; if (replacement != null && invocation.Expression is MemberAccessExpressionSyntax) { diff --git a/src/xunit.analyzers.fixes/AssertStringEqualityCheckShouldNotUseBoolCheckFixer.cs b/src/xunit.analyzers.fixes/AssertStringEqualityCheckShouldNotUseBoolCheckFixer.cs index d195f828..baf91e07 100644 --- a/src/xunit.analyzers.fixes/AssertStringEqualityCheckShouldNotUseBoolCheckFixer.cs +++ b/src/xunit.analyzers.fixes/AssertStringEqualityCheckShouldNotUseBoolCheckFixer.cs @@ -33,7 +33,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var assertMethodName = diagnostic.Properties[Constants.Properties.AssertMethodName]; var isStaticMethodCall = diagnostic.Properties[Constants.Properties.IsStaticMethodCall]; var ignoreCase = diagnostic.Properties[Constants.Properties.IgnoreCase]; - var replacement = GetReplacementMethodName(assertMethodName); + var replacement = diagnostic.Properties[Constants.Properties.Replacement]; context.RegisterCodeFix( CodeAction.Create( @@ -45,9 +45,6 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) ); } - static string GetReplacementMethodName(string assertMethodName) => - assertMethodName == Constants.Asserts.True ? Constants.Asserts.Equal : Constants.Asserts.NotEqual; - static async Task UseEqualCheck( Document document, InvocationExpressionSyntax invocation, diff --git a/src/xunit.analyzers.fixes/AssertSubstringCheckShouldNotUseBoolCheckFixer.cs b/src/xunit.analyzers.fixes/AssertSubstringCheckShouldNotUseBoolCheckFixer.cs index c58d3620..3c1106db 100644 --- a/src/xunit.analyzers.fixes/AssertSubstringCheckShouldNotUseBoolCheckFixer.cs +++ b/src/xunit.analyzers.fixes/AssertSubstringCheckShouldNotUseBoolCheckFixer.cs @@ -30,7 +30,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var diagnostic = context.Diagnostics.First(); var assertMethodName = diagnostic.Properties[Constants.Properties.AssertMethodName]; var substringMethodName = diagnostic.Properties[Constants.Properties.SubstringMethodName]; - var replacement = GetReplacementMethodName(assertMethodName, substringMethodName); + var replacement = diagnostic.Properties[Constants.Properties.Replacement]; var title = string.Format(titleTemplate, replacement); context.RegisterCodeFix( @@ -43,16 +43,6 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) ); } - static string GetReplacementMethodName( - string assertMethodName, - string substringMethodName) - { - if (substringMethodName == nameof(string.Contains)) - return assertMethodName == Constants.Asserts.True ? Constants.Asserts.Contains : Constants.Asserts.DoesNotContain; - - return substringMethodName; - } - static async Task UseSubstringCheckAsync( Document document, InvocationExpressionSyntax invocation, diff --git a/src/xunit.analyzers.fixes/AssertThrowsShouldNotBeUsedForAsyncThrowsCheckFixer.cs b/src/xunit.analyzers.fixes/AssertThrowsShouldNotBeUsedForAsyncThrowsCheckFixer.cs index 58ef2337..1a02fdf1 100644 --- a/src/xunit.analyzers.fixes/AssertThrowsShouldNotBeUsedForAsyncThrowsCheckFixer.cs +++ b/src/xunit.analyzers.fixes/AssertThrowsShouldNotBeUsedForAsyncThrowsCheckFixer.cs @@ -31,12 +31,13 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var method = invocation.FirstAncestorOrSelf(); var diagnostic = context.Diagnostics.First(); var methodName = diagnostic.Properties[Constants.Properties.MethodName]; - var title = string.Format(TitleTemplate, methodName + "Async"); + var replacement = diagnostic.Properties[Constants.Properties.Replacement]; + var title = string.Format(TitleTemplate, replacement); context.RegisterCodeFix( CodeAction.Create( title, - createChangedDocument: ct => UseAsyncThrowsCheck(context.Document, invocation, method, methodName + "Async", ct), + createChangedDocument: ct => UseAsyncThrowsCheck(context.Document, invocation, method, replacement, ct), equivalenceKey: title ), context.Diagnostics diff --git a/src/xunit.analyzers.tests/Analyzers/AssertCollectionContainsShouldNotUseBoolCheckTests.cs b/src/xunit.analyzers.tests/Analyzers/AssertCollectionContainsShouldNotUseBoolCheckTests.cs index abc7a1ce..b2d9b222 100644 --- a/src/xunit.analyzers.tests/Analyzers/AssertCollectionContainsShouldNotUseBoolCheckTests.cs +++ b/src/xunit.analyzers.tests/Analyzers/AssertCollectionContainsShouldNotUseBoolCheckTests.cs @@ -1,4 +1,5 @@ using Xunit; +using Xunit.Analyzers; using Verify = CSharpVerifier; public class AssertCollectionContainsShouldNotUseBoolCheckTests @@ -29,7 +30,7 @@ void TestMethod() {{ Verify .Diagnostic() .WithSpan(4, 9, 4, 40 + collection.Length) - .WithArguments("Assert.True()"); + .WithArguments("Assert.True()", Constants.Asserts.Contains); await Verify.VerifyAnalyzerAsync(source, expected); } @@ -48,7 +49,7 @@ void TestMethod() {{ Verify .Diagnostic() .WithSpan(4, 9, 4, 41 + collection.Length) - .WithArguments("Assert.False()"); + .WithArguments("Assert.False()", Constants.Asserts.DoesNotContain); await Verify.VerifyAnalyzerAsync(source, expected); } @@ -69,7 +70,7 @@ void TestMethod() {{ Verify .Diagnostic() .WithSpan(6, 9, 6, 40 + enumerable.Length) - .WithArguments("Assert.True()"); + .WithArguments("Assert.True()", Constants.Asserts.Contains); await Verify.VerifyAnalyzerAsync(source, expected); } @@ -90,7 +91,7 @@ void TestMethod() {{ Verify .Diagnostic() .WithSpan(6, 9, 6, 98 + enumerable.Length) - .WithArguments("Assert.True()"); + .WithArguments("Assert.True()", Constants.Asserts.Contains); await Verify.VerifyAnalyzerAsync(source, expected); } @@ -111,7 +112,7 @@ void TestMethod() {{ Verify .Diagnostic() .WithSpan(6, 9, 6, 41 + enumerable.Length) - .WithArguments("Assert.False()"); + .WithArguments("Assert.False()", Constants.Asserts.DoesNotContain); await Verify.VerifyAnalyzerAsync(source, expected); } @@ -132,7 +133,7 @@ void TestMethod() {{ Verify .Diagnostic() .WithSpan(6, 9, 6, 99 + enumerable.Length) - .WithArguments("Assert.False()"); + .WithArguments("Assert.False()", Constants.Asserts.DoesNotContain); await Verify.VerifyAnalyzerAsync(source, expected); } diff --git a/src/xunit.analyzers.tests/Analyzers/AssertEqualShouldNotBeUsedForBoolLiteralCheckTests.cs b/src/xunit.analyzers.tests/Analyzers/AssertEqualShouldNotBeUsedForBoolLiteralCheckTests.cs index d6c0ba63..21ffb0c0 100644 --- a/src/xunit.analyzers.tests/Analyzers/AssertEqualShouldNotBeUsedForBoolLiteralCheckTests.cs +++ b/src/xunit.analyzers.tests/Analyzers/AssertEqualShouldNotBeUsedForBoolLiteralCheckTests.cs @@ -14,8 +14,13 @@ public class AssertEqualShouldNotBeUsedForBoolLiteralCheckTests }; [Theory] - [MemberData(nameof(Methods))] - public async void FindsWarning_ForFirstBoolLiteral(string method) + [InlineData(Constants.Asserts.Equal, Constants.Asserts.True)] + [InlineData(Constants.Asserts.StrictEqual, Constants.Asserts.True)] + [InlineData(Constants.Asserts.NotEqual, Constants.Asserts.False)] + [InlineData(Constants.Asserts.NotStrictEqual, Constants.Asserts.False)] + public async void FindsWarning_ForFirstBoolLiteral( + string method, + string replacement) { var source = $@" class TestClass {{ @@ -29,15 +34,17 @@ void TestMethod() {{ .Diagnostic() .WithSpan(5, 9, 5, 33 + method.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments($"Assert.{method}()"); + .WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzerAsync(source, expected); } [Theory] - [InlineData(Constants.Asserts.Equal)] - [InlineData(Constants.Asserts.NotEqual)] - public async void FindsWarning_ForFirstBoolLiteral_WithCustomComparer(string method) + [InlineData(Constants.Asserts.Equal, Constants.Asserts.False)] + [InlineData(Constants.Asserts.NotEqual, Constants.Asserts.True)] + public async void FindsWarning_ForFirstBoolLiteral_WithCustomComparer( + string method, + string replacement) { var source = $@" class TestClass {{ @@ -51,7 +58,7 @@ void TestMethod() {{ .Diagnostic() .WithSpan(5, 9, 5, 93 + method.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments($"Assert.{method}()"); + .WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzerAsync(source, expected); } diff --git a/src/xunit.analyzers.tests/Analyzers/AssertEqualShouldNotBeUsedForCollectionSizeCheckTests.cs b/src/xunit.analyzers.tests/Analyzers/AssertEqualShouldNotBeUsedForCollectionSizeCheckTests.cs index 9a88cab9..9a4100c0 100644 --- a/src/xunit.analyzers.tests/Analyzers/AssertEqualShouldNotBeUsedForCollectionSizeCheckTests.cs +++ b/src/xunit.analyzers.tests/Analyzers/AssertEqualShouldNotBeUsedForCollectionSizeCheckTests.cs @@ -1,5 +1,6 @@ using Microsoft.CodeAnalysis; using Xunit; +using Xunit.Analyzers; using Verify = CSharpVerifier; public class AssertEqualShouldNotBeUsedForCollectionSizeCheckTests @@ -66,7 +67,7 @@ void TestMethod() {{ .Diagnostic() .WithSpan(6, 9, 6, 32 + collection.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments("Assert.Equal()"); + .WithArguments("Assert.Equal()", Constants.Asserts.Empty); await Verify.VerifyAnalyzerAsync(source, expected); } @@ -88,7 +89,7 @@ void TestMethod() {{ .Diagnostic() .WithSpan(6, 9, 6, 35 + collection.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments("Assert.NotEqual()"); + .WithArguments("Assert.NotEqual()", Constants.Asserts.NotEmpty); await Verify.VerifyAnalyzerAsync(source, expected); } @@ -110,7 +111,7 @@ void TestMethod() {{ .Diagnostic() .WithSpan(6, 9, 6, 32 + collection.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments("Assert.Equal()"); + .WithArguments("Assert.Equal()", Constants.Asserts.Single); await Verify.VerifyAnalyzerAsync(source, expected); } @@ -145,7 +146,7 @@ void TestMethod() { .Diagnostic() .WithSpan(20, 9, 20, 51) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments("Assert.Equal()"); + .WithArguments("Assert.Equal()", Constants.Asserts.Single); await Verify.VerifyAnalyzerAsync(source, expected); } diff --git a/src/xunit.analyzers.tests/Analyzers/AssertEqualShouldNotBeUsedForNullCheckTests.cs b/src/xunit.analyzers.tests/Analyzers/AssertEqualShouldNotBeUsedForNullCheckTests.cs index f86bd3bc..a82083e7 100644 --- a/src/xunit.analyzers.tests/Analyzers/AssertEqualShouldNotBeUsedForNullCheckTests.cs +++ b/src/xunit.analyzers.tests/Analyzers/AssertEqualShouldNotBeUsedForNullCheckTests.cs @@ -14,15 +14,17 @@ public class AssertEqualShouldNotBeUsedForNullCheckTests Constants.Asserts.Same, Constants.Asserts.NotSame, }; - public static TheoryData Methods_Equal = new() + public static TheoryData Methods_Equal_WithReplacement = new() { - Constants.Asserts.Equal, - Constants.Asserts.NotEqual, + { Constants.Asserts.Equal, Constants.Asserts.Null }, + { Constants.Asserts.NotEqual, Constants.Asserts.NotNull } }; [Theory] - [MemberData(nameof(Methods_Equal))] - public async void FindsWarning_ForFirstNullLiteral_StringOverload(string method) + [MemberData(nameof(Methods_Equal_WithReplacement))] + public async void FindsWarning_ForFirstNullLiteral_StringOverload( + string method, + string replacement) { var source = $@" class TestClass {{ @@ -36,14 +38,16 @@ void TestMethod() {{ .Diagnostic() .WithSpan(5, 9, 5, 33 + method.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments($"Assert.{method}()"); + .WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzerAsync(source, expected); } [Theory] - [MemberData(nameof(Methods_Equal))] - public async void FindsWarning_ForFirstNullLiteral_StringOverload_WithCustomComparer(string method) + [MemberData(nameof(Methods_Equal_WithReplacement))] + public async void FindsWarning_ForFirstNullLiteral_StringOverload_WithCustomComparer( + string method, + string replacement) { var source = $@" class TestClass {{ @@ -57,14 +61,21 @@ void TestMethod() {{ .Diagnostic() .WithSpan(5, 9, 5, 64 + method.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments($"Assert.{method}()"); + .WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzerAsync(source, expected); } [Theory] - [MemberData(nameof(Methods_All))] - public async void FindsWarning_ForFirstNullLiteral_ObjectOverload(string method) + [InlineData(Constants.Asserts.Equal, Constants.Asserts.Null)] + [InlineData(Constants.Asserts.StrictEqual, Constants.Asserts.Null)] + [InlineData(Constants.Asserts.Same, Constants.Asserts.Null)] + [InlineData(Constants.Asserts.NotEqual, Constants.Asserts.NotNull)] + [InlineData(Constants.Asserts.NotStrictEqual, Constants.Asserts.NotNull)] + [InlineData(Constants.Asserts.NotSame, Constants.Asserts.NotNull)] + public async void FindsWarning_ForFirstNullLiteral_ObjectOverload( + string method, + string replacement) { var source = $@" class TestClass {{ @@ -78,14 +89,16 @@ void TestMethod() {{ .Diagnostic() .WithSpan(5, 9, 5, 33 + method.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments($"Assert.{method}()"); + .WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzerAsync(source, expected); } [Theory] - [MemberData(nameof(Methods_Equal))] - public async void FindsWarning_ForFirstNullLiteral_ObjectOverload_WithCustomComparer(string method) + [MemberData(nameof(Methods_Equal_WithReplacement))] + public async void FindsWarning_ForFirstNullLiteral_ObjectOverload_WithCustomComparer( + string method, + string replacement) { var source = $@" class TestClass {{ @@ -99,17 +112,19 @@ void TestMethod() {{ .Diagnostic() .WithSpan(5, 9, 5, 94 + method.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments($"Assert.{method}()"); + .WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzerAsync(source, expected); } [Theory] - [InlineData(Constants.Asserts.Equal)] - [InlineData(Constants.Asserts.NotEqual)] - [InlineData(Constants.Asserts.StrictEqual)] - [InlineData(Constants.Asserts.NotStrictEqual)] - public async void FindsWarning_ForFirstNullLiteral_GenericOverload(string method) + [InlineData(Constants.Asserts.Equal, Constants.Asserts.Null)] + [InlineData(Constants.Asserts.NotEqual, Constants.Asserts.NotNull)] + [InlineData(Constants.Asserts.StrictEqual, Constants.Asserts.Null)] + [InlineData(Constants.Asserts.NotStrictEqual, Constants.Asserts.NotNull)] + public async void FindsWarning_ForFirstNullLiteral_GenericOverload( + string method, + string replacement) { var source = $@" class TestClass {{ @@ -123,14 +138,16 @@ void TestMethod() {{ .Diagnostic() .WithSpan(5, 9, 5, 44 + method.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments($"Assert.{method}()"); + .WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzerAsync(source, expected); } [Theory] - [MemberData(nameof(Methods_Equal))] - public async void FindsWarning_ForFirstNullLiteral_GenericOverload_WithCustomComparer(string method) + [MemberData(nameof(Methods_Equal_WithReplacement))] + public async void FindsWarning_ForFirstNullLiteral_GenericOverload_WithCustomComparer( + string method, + string replacement) { var source = $@" class TestClass {{ @@ -144,7 +161,7 @@ void TestMethod() {{ .Diagnostic() .WithSpan(5, 9, 5, 108 + method.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments($"Assert.{method}()"); + .WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzerAsync(source, expected); } diff --git a/src/xunit.analyzers.tests/Analyzers/AssertEqualsShouldNotBeUsedTests.cs b/src/xunit.analyzers.tests/Analyzers/AssertEqualsShouldNotBeUsedTests.cs index 18399c1a..55945211 100644 --- a/src/xunit.analyzers.tests/Analyzers/AssertEqualsShouldNotBeUsedTests.cs +++ b/src/xunit.analyzers.tests/Analyzers/AssertEqualsShouldNotBeUsedTests.cs @@ -28,7 +28,7 @@ void TestMethod() {{ .Diagnostic() .WithSpan(4, 9, 4, 34 + method.Length) .WithSeverity(DiagnosticSeverity.Hidden) - .WithArguments($"Assert.{method}()"), + .WithArguments($"Assert.{method}()", replacement), }; await Verify.VerifyAnalyzerAsync(source, expected); diff --git a/src/xunit.analyzers.tests/Analyzers/AssertRegexMatchShouldNotUseBoolLiteralCheckTests.cs b/src/xunit.analyzers.tests/Analyzers/AssertRegexMatchShouldNotUseBoolLiteralCheckTests.cs index c88487fc..050724c8 100644 --- a/src/xunit.analyzers.tests/Analyzers/AssertRegexMatchShouldNotUseBoolLiteralCheckTests.cs +++ b/src/xunit.analyzers.tests/Analyzers/AssertRegexMatchShouldNotUseBoolLiteralCheckTests.cs @@ -5,15 +5,17 @@ public class AssertRegexMatchShouldNotUseBoolLiteralCheckTests { - public static TheoryData Methods = new() + public static TheoryData Methods_WithReplacement = new() { - Constants.Asserts.True, - Constants.Asserts.False, + { Constants.Asserts.True, Constants.Asserts.Matches }, + { Constants.Asserts.False, Constants.Asserts.DoesNotMatch }, }; [Theory] - [MemberData(nameof(Methods))] - public async void FindsWarning_ForStaticRegexIsMatch(string method) + [MemberData(nameof(Methods_WithReplacement))] + public async void FindsWarning_ForStaticRegexIsMatch( + string method, + string replacement) { var source = $@" class TestClass {{ @@ -26,14 +28,16 @@ void TestMethod() {{ .Diagnostic() .WithSpan(4, 9, 4, 83 + method.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments($"Assert.{method}()"); + .WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzerAsync(source, expected); } [Theory] - [MemberData(nameof(Methods))] - public async void FindsWarning_ForInstanceRegexIsMatchWithInlineConstructedRegex(string method) + [MemberData(nameof(Methods_WithReplacement))] + public async void FindsWarning_ForInstanceRegexIsMatchWithInlineConstructedRegex( + string method, + string replacement) { var source = $@" class TestClass {{ @@ -46,14 +50,16 @@ void TestMethod() {{ .Diagnostic() .WithSpan(4, 9, 4, 87 + method.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments($"Assert.{method}()"); + .WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzerAsync(source, expected); } [Theory] - [MemberData(nameof(Methods))] - public async void FindsWarning_ForInstanceRegexIsMatchWithConstructedRegexVariable(string method) + [MemberData(nameof(Methods_WithReplacement))] + public async void FindsWarning_ForInstanceRegexIsMatchWithConstructedRegexVariable( + string method, + string replacement) { var source = $@" class TestClass {{ @@ -67,7 +73,7 @@ void TestMethod() {{ .Diagnostic() .WithSpan(5, 9, 5, 45 + method.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments($"Assert.{method}()"); + .WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzerAsync(source, expected); } diff --git a/src/xunit.analyzers.tests/Analyzers/AssertSameShouldNotBeCalledOnValueTypesTests.cs b/src/xunit.analyzers.tests/Analyzers/AssertSameShouldNotBeCalledOnValueTypesTests.cs index e6fc8c2b..a9d98d72 100644 --- a/src/xunit.analyzers.tests/Analyzers/AssertSameShouldNotBeCalledOnValueTypesTests.cs +++ b/src/xunit.analyzers.tests/Analyzers/AssertSameShouldNotBeCalledOnValueTypesTests.cs @@ -5,15 +5,17 @@ public class AssertSameShouldNotBeCalledOnValueTypesTests { - public static TheoryData Methods = new() + public static TheoryData Methods_WithReplacement = new() { - Constants.Asserts.Same, - Constants.Asserts.NotSame, + { Constants.Asserts.Same, Constants.Asserts.Equal }, + { Constants.Asserts.NotSame, Constants.Asserts.NotEqual }, }; [Theory] - [MemberData(nameof(Methods))] - public async void FindsWarningForTwoValueParameters(string method) + [MemberData(nameof(Methods_WithReplacement))] + public async void FindsWarningForTwoValueParameters( + string method, + string replacement) { var source = $@" class TestClass {{ @@ -27,14 +29,16 @@ void TestMethod() {{ .Diagnostic() .WithSpan(5, 9, 5, 28 + method.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments($"Assert.{method}()", "int"); + .WithArguments($"Assert.{method}()", "int", replacement); await Verify.VerifyAnalyzerAsync(source, expected); } [Theory] - [MemberData(nameof(Methods))] - public async void FindsWarningForFirstValueParameters(string method) + [MemberData(nameof(Methods_WithReplacement))] + public async void FindsWarningForFirstValueParameters( + string method, + string replacement) { var source = $@" class TestClass {{ @@ -48,14 +52,16 @@ void TestMethod() {{ .Diagnostic() .WithSpan(5, 9, 5, 28 + method.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments($"Assert.{method}()", "int"); + .WithArguments($"Assert.{method}()", "int", replacement); await Verify.VerifyAnalyzerAsync(source, expected); } [Theory] - [MemberData(nameof(Methods))] - public async void FindsWarningForSecondValueParameters(string method) + [MemberData(nameof(Methods_WithReplacement))] + public async void FindsWarningForSecondValueParameters( + string method, + string replacement) { var source = $@" class TestClass {{ @@ -69,7 +75,7 @@ void TestMethod() {{ .Diagnostic() .WithSpan(5, 9, 5, 28 + method.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments($"Assert.{method}()", "int"); + .WithArguments($"Assert.{method}()", "int", replacement); await Verify.VerifyAnalyzerAsync(source, expected); } diff --git a/src/xunit.analyzers.tests/Analyzers/AssertStringEqualityCheckShouldNotUseBoolCheckTest.cs b/src/xunit.analyzers.tests/Analyzers/AssertStringEqualityCheckShouldNotUseBoolCheckTest.cs index 3d47226e..1c0ad486 100644 --- a/src/xunit.analyzers.tests/Analyzers/AssertStringEqualityCheckShouldNotUseBoolCheckTest.cs +++ b/src/xunit.analyzers.tests/Analyzers/AssertStringEqualityCheckShouldNotUseBoolCheckTest.cs @@ -6,10 +6,10 @@ public class AssertStringEqualityCheckShouldNotUseBoolCheckTest { - public static TheoryData Methods = new() + public static TheoryData Methods_WithReplacement = new() { - Constants.Asserts.True, - Constants.Asserts.False, + { Constants.Asserts.True, Constants.Asserts.Equal }, + { Constants.Asserts.False, Constants.Asserts.NotEqual }, }; public static TheoryData SupportedStringComparisons = new() { @@ -34,8 +34,10 @@ public class AssertStringEqualityCheckShouldNotUseBoolCheckTest }; [Theory] - [MemberData(nameof(Methods))] - public async void FindsWarning_ForInstanceEqualsCheck(string method) + [MemberData(nameof(Methods_WithReplacement))] + public async void FindsWarning_ForInstanceEqualsCheck( + string method, + string replacement) { var source = $@" class TestClass {{ @@ -48,7 +50,7 @@ void TestMethod() {{ .Diagnostic() .WithSpan(4, 9, 4, 41 + method.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments($"Assert.{method}()"); + .WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzerAsync(source, expected); } @@ -68,7 +70,7 @@ void TestMethod() {{ .Diagnostic() .WithSpan(4, 9, 4, 71 + comparison.ToString().Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments($"Assert.True()"); + .WithArguments("Assert.True()", Constants.Asserts.Equal); await Verify.VerifyAnalyzerAsync(source, expected); } @@ -102,8 +104,10 @@ void TestMethod() {{ } [Theory] - [MemberData(nameof(Methods))] - public async void FindsWarning_ForStaticEqualsCheck(string method) + [MemberData(nameof(Methods_WithReplacement))] + public async void FindsWarning_ForStaticEqualsCheck( + string method, + string replacement) { var source = $@" class TestClass {{ @@ -116,7 +120,7 @@ void TestMethod() {{ .Diagnostic() .WithSpan(4, 9, 4, 56 + method.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments($"Assert.{method}()"); + .WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzerAsync(source, expected); } @@ -136,7 +140,7 @@ void TestMethod() {{ .Diagnostic() .WithSpan(4, 9, 4, 86 + comparison.ToString().Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments($"Assert.True()"); + .WithArguments("Assert.True()", Constants.Asserts.Equal); await Verify.VerifyAnalyzerAsync(source, expected); } diff --git a/src/xunit.analyzers.tests/Analyzers/AssertSubstringCheckShouldNotUseBoolCheckTests.cs b/src/xunit.analyzers.tests/Analyzers/AssertSubstringCheckShouldNotUseBoolCheckTests.cs index eed77672..f3b7d87c 100644 --- a/src/xunit.analyzers.tests/Analyzers/AssertSubstringCheckShouldNotUseBoolCheckTests.cs +++ b/src/xunit.analyzers.tests/Analyzers/AssertSubstringCheckShouldNotUseBoolCheckTests.cs @@ -12,8 +12,11 @@ public class AssertSubstringCheckShouldNotUseBoolCheckTests }; [Theory] - [MemberData(nameof(Methods))] - public async void FindsWarning_ForBooleanContainsCheck(string method) + [InlineData(Constants.Asserts.True, Constants.Asserts.Contains)] + [InlineData(Constants.Asserts.False, Constants.Asserts.DoesNotContain)] + public async void FindsWarning_ForBooleanContainsCheck( + string method, + string replacement) { var source = $@" class TestClass {{ @@ -26,7 +29,7 @@ void TestMethod() {{ .Diagnostic() .WithSpan(4, 9, 4, 43 + method.Length) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments($"Assert.{method}()"); + .WithArguments($"Assert.{method}()", replacement); await Verify.VerifyAnalyzerAsync(source, expected); } @@ -59,7 +62,7 @@ void TestMethod() { .Diagnostic() .WithSpan(4, 9, 4, 49) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments("Assert.True()"); + .WithArguments("Assert.True()", Constants.Asserts.StartsWith); await Verify.VerifyAnalyzerAsync(source, expected); } @@ -78,7 +81,7 @@ void TestMethod() { .Diagnostic() .WithSpan(4, 9, 4, 89) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments("Assert.True()"); + .WithArguments("Assert.True()", Constants.Asserts.StartsWith); await Verify.VerifyAnalyzerAsync(source, expected); } @@ -179,7 +182,7 @@ void TestMethod() { .Diagnostic() .WithSpan(4, 9, 4, 47) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments("Assert.True()"); + .WithArguments("Assert.True()", Constants.Asserts.EndsWith); await Verify.VerifyAnalyzerAsync(source, expected); } @@ -198,7 +201,7 @@ void TestMethod() { .Diagnostic() .WithSpan(4, 9, 4, 87) .WithSeverity(DiagnosticSeverity.Warning) - .WithArguments("Assert.True()"); + .WithArguments("Assert.True()", Constants.Asserts.EndsWith); await Verify.VerifyAnalyzerAsync(source, expected); } diff --git a/src/xunit.analyzers.tests/Analyzers/AssertThrowsShouldNotBeUsedForAsyncThrowsCheckTests.cs b/src/xunit.analyzers.tests/Analyzers/AssertThrowsShouldNotBeUsedForAsyncThrowsCheckTests.cs index dde28307..4fce0084 100644 --- a/src/xunit.analyzers.tests/Analyzers/AssertThrowsShouldNotBeUsedForAsyncThrowsCheckTests.cs +++ b/src/xunit.analyzers.tests/Analyzers/AssertThrowsShouldNotBeUsedForAsyncThrowsCheckTests.cs @@ -1,5 +1,6 @@ using Microsoft.CodeAnalysis; using Xunit; +using Xunit.Analyzers; using Verify = CSharpVerifier; public class AssertThrowsShouldNotBeUsedForAsyncThrowsCheckTests @@ -90,7 +91,7 @@ void TestMethod() {{ .Diagnostic() .WithSpan(8, 9, 8, 70 + lambda.Length) .WithSeverity(DiagnosticSeverity.Error) - .WithArguments("Assert.Throws()"); + .WithArguments("Assert.Throws()", Constants.Asserts.ThrowsAsync); await Verify.VerifyAnalyzerAsync(source, expected); } @@ -119,7 +120,7 @@ void TestMethod() {{ .Diagnostic() .WithSpan(8, 9, 8, 62 + lambda.Length) .WithSeverity(DiagnosticSeverity.Error) - .WithArguments("Assert.Throws()"), + .WithArguments("Assert.Throws()", Constants.Asserts.ThrowsAsync), }; await Verify.VerifyAnalyzerAsync(source, expected); @@ -149,7 +150,7 @@ void TestMethod() {{ .Diagnostic() .WithSpan(8, 9, 8, 66 + lambda.Length) .WithSeverity(DiagnosticSeverity.Error) - .WithArguments("Assert.Throws()"), + .WithArguments("Assert.Throws()", Constants.Asserts.ThrowsAsync), }; await Verify.VerifyAnalyzerAsync(source, expected); @@ -228,7 +229,7 @@ void TestMethod() {{ .Diagnostic() .WithSpan(8, 9, 8, 65 + lambda.Length) .WithSeverity(DiagnosticSeverity.Error) - .WithArguments("Assert.ThrowsAny()"); + .WithArguments("Assert.ThrowsAny()", Constants.Asserts.ThrowsAnyAsync); await Verify.VerifyAnalyzerAsync(source, expected); } diff --git a/src/xunit.analyzers.tests/Analyzers/AssertThrowsShouldUseGenericOverloadCheckTests.cs b/src/xunit.analyzers.tests/Analyzers/AssertThrowsShouldUseGenericOverloadCheckTests.cs index fb540e6d..9756b75b 100644 --- a/src/xunit.analyzers.tests/Analyzers/AssertThrowsShouldUseGenericOverloadCheckTests.cs +++ b/src/xunit.analyzers.tests/Analyzers/AssertThrowsShouldUseGenericOverloadCheckTests.cs @@ -30,7 +30,8 @@ void TestMethod() {{ Verify .Diagnostic() .WithSpan(8, 9, 8, 78 + method.Length) - .WithSeverity(DiagnosticSeverity.Warning); + .WithSeverity(DiagnosticSeverity.Warning) + .WithArguments(method, "System.NotImplementedException"); await Verify.VerifyAnalyzerAsync(source, expected); } @@ -49,7 +50,8 @@ void TestMethod() {{ Verify .Diagnostic() .WithSpan(4, 9, 4, 106 + method.Length) - .WithSeverity(DiagnosticSeverity.Warning); + .WithSeverity(DiagnosticSeverity.Warning) + .WithArguments(method, "System.NotImplementedException"); await Verify.VerifyAnalyzerAsync(source, expected); } diff --git a/src/xunit.analyzers/AssertCollectionContainsShouldNotUseBoolCheck.cs b/src/xunit.analyzers/AssertCollectionContainsShouldNotUseBoolCheck.cs index 0a7949bd..65ba480d 100644 --- a/src/xunit.analyzers/AssertCollectionContainsShouldNotUseBoolCheck.cs +++ b/src/xunit.analyzers/AssertCollectionContainsShouldNotUseBoolCheck.cs @@ -45,8 +45,14 @@ protected override void Analyze( if (!IsLinqContainsMethod(methodSymbol) && !IsICollectionContainsMethod(context, methodSymbol)) return; + var replacement = + method.Name == Constants.Asserts.True + ? Constants.Asserts.Contains + : Constants.Asserts.DoesNotContain; + var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MethodName] = method.Name; + builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( @@ -55,8 +61,12 @@ protected override void Analyze( builder.ToImmutable(), SymbolDisplay.ToDisplayString( method, - SymbolDisplayFormat.CSharpShortErrorMessageFormat.WithParameterOptions(SymbolDisplayParameterOptions.None).WithGenericsOptions(SymbolDisplayGenericsOptions.None) - ) + SymbolDisplayFormat + .CSharpShortErrorMessageFormat + .WithParameterOptions(SymbolDisplayParameterOptions.None) + .WithGenericsOptions(SymbolDisplayGenericsOptions.None) + ), + replacement ) ); } diff --git a/src/xunit.analyzers/AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheck.cs b/src/xunit.analyzers/AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheck.cs index dc879014..2256ce5a 100644 --- a/src/xunit.analyzers/AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheck.cs +++ b/src/xunit.analyzers/AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheck.cs @@ -35,8 +35,14 @@ protected override void Analyze( if (SymbolDisplay.ToDisplayString(methodSymbol.OriginalDefinition) != enumerableAnyExtensionMethod) return; + var replacement = + method.Name == Constants.Asserts.True + ? Constants.Asserts.Contains + : Constants.Asserts.DoesNotContain; + var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.AssertMethodName] = method.Name; + builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( @@ -49,7 +55,8 @@ protected override void Analyze( .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) - ) + ), + replacement ) ); } diff --git a/src/xunit.analyzers/AssertEqualGenericShouldNotBeUsedForStringValue.cs b/src/xunit.analyzers/AssertEqualGenericShouldNotBeUsedForStringValue.cs index c5b2ee99..f2cb3751 100644 --- a/src/xunit.analyzers/AssertEqualGenericShouldNotBeUsedForStringValue.cs +++ b/src/xunit.analyzers/AssertEqualGenericShouldNotBeUsedForStringValue.cs @@ -34,13 +34,21 @@ protected override void Analyze( !method.Parameters[1].Type.SpecialType.Equals(SpecialType.System_String))) return; - var invalidUsageDescription = method.Name == Constants.Asserts.Equal ? "generic Assert.Equal overload" : "Assert.StrictEqual"; + var invalidUsageDescription = + method.Name == Constants.Asserts.Equal + ? "Assert.Equal" + : "Assert.StrictEqual"; + var replacement = + method.Name == Constants.Asserts.Equal + ? "non-generic Assert.Equal" + : "Assert.Equal"; context.ReportDiagnostic( Diagnostic.Create( Descriptors.X2006_AssertEqualGenericShouldNotBeUsedForStringValue, invocationOperation.Syntax.GetLocation(), - invalidUsageDescription + invalidUsageDescription, + replacement ) ); } diff --git a/src/xunit.analyzers/AssertEqualShouldNotBeUsedForBoolLiteralCheck.cs b/src/xunit.analyzers/AssertEqualShouldNotBeUsedForBoolLiteralCheck.cs index 1461fc34..84fe7ae3 100644 --- a/src/xunit.analyzers/AssertEqualShouldNotBeUsedForBoolLiteralCheck.cs +++ b/src/xunit.analyzers/AssertEqualShouldNotBeUsedForBoolLiteralCheck.cs @@ -11,18 +11,17 @@ namespace Xunit.Analyzers [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertEqualShouldNotBeUsedForBoolLiteralCheck : AssertUsageAnalyzerBase { - public static readonly HashSet EqualMethods = new() + static readonly HashSet equalMethods = new() { Constants.Asserts.Equal, Constants.Asserts.StrictEqual }; - public static readonly HashSet NotEqualMethods = new() + static readonly HashSet notEqualMethods = new() { Constants.Asserts.NotEqual, Constants.Asserts.NotStrictEqual }; - - static readonly string[] targetMethods = EqualMethods.Union(NotEqualMethods).ToArray(); + static readonly string[] targetMethods = equalMethods.Union(notEqualMethods).ToArray(); public AssertEqualShouldNotBeUsedForBoolLiteralCheck() : base(Descriptors.X2004_AssertEqualShouldNotUsedForBoolLiteralCheck, targetMethods) @@ -51,9 +50,12 @@ protected override void Analyze( if (!(isTrue ^ isFalse)) return; + var replacement = GetReplacementMethodName(method.Name, isTrue); + var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MethodName] = method.Name; builder[Constants.Properties.LiteralValue] = isTrue ? bool.TrueString : bool.FalseString; + builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( @@ -66,9 +68,22 @@ protected override void Analyze( .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) - ) + ), + replacement ) ); } + + static string GetReplacementMethodName( + string methodName, + bool isTrue) + { + if (equalMethods.Contains(methodName)) + return isTrue ? Constants.Asserts.True : Constants.Asserts.False; + if (notEqualMethods.Contains(methodName)) + return isTrue ? Constants.Asserts.False : Constants.Asserts.True; + + return null; + } } } diff --git a/src/xunit.analyzers/AssertEqualShouldNotBeUsedForCollectionSizeCheck.cs b/src/xunit.analyzers/AssertEqualShouldNotBeUsedForCollectionSizeCheck.cs index 4eeb9bc4..df8324e4 100644 --- a/src/xunit.analyzers/AssertEqualShouldNotBeUsedForCollectionSizeCheck.cs +++ b/src/xunit.analyzers/AssertEqualShouldNotBeUsedForCollectionSizeCheck.cs @@ -70,9 +70,12 @@ protected override void Analyze( !IsIReadOnlyCollectionOfTCountProperty(context, symbol)) return; + var replacement = GetReplacementMethodName(method.Name, size); + var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MethodName] = method.Name; builder[Constants.Properties.SizeValue] = size.ToString(); + builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( @@ -85,11 +88,22 @@ protected override void Analyze( .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) - ) + ), + replacement ) ); } + static string GetReplacementMethodName( + string methodName, + int size) + { + if (size == 1) + return Constants.Asserts.Single; + + return methodName == Constants.Asserts.Equal ? Constants.Asserts.Empty : Constants.Asserts.NotEmpty; + } + static bool IsCollectionsWithExceptionThrowingGetEnumeratorMethod(ISymbol symbol) => collectionTypesWithExceptionThrowingGetEnumeratorMethod.Contains(symbol.ContainingType.ConstructedFrom.ToDisplayString()); diff --git a/src/xunit.analyzers/AssertEqualShouldNotBeUsedForNullCheck.cs b/src/xunit.analyzers/AssertEqualShouldNotBeUsedForNullCheck.cs index 47d3eb62..b05c7e61 100644 --- a/src/xunit.analyzers/AssertEqualShouldNotBeUsedForNullCheck.cs +++ b/src/xunit.analyzers/AssertEqualShouldNotBeUsedForNullCheck.cs @@ -12,20 +12,19 @@ namespace Xunit.Analyzers [DiagnosticAnalyzer(LanguageNames.CSharp)] public class AssertEqualShouldNotBeUsedForNullCheck : AssertUsageAnalyzerBase { - public static readonly HashSet EqualMethods = new() + static readonly HashSet equalMethods = new() { Constants.Asserts.Equal, Constants.Asserts.StrictEqual, Constants.Asserts.Same }; - public static readonly HashSet NotEqualMethods = new() + static readonly HashSet notEqualMethods = new() { Constants.Asserts.NotEqual, Constants.Asserts.NotStrictEqual, Constants.Asserts.NotSame }; - - static readonly string[] targetMethods = EqualMethods.Union(NotEqualMethods).ToArray(); + static readonly string[] targetMethods = equalMethods.Union(notEqualMethods).ToArray(); public AssertEqualShouldNotBeUsedForNullCheck() : base(Descriptors.X2003_AssertEqualShouldNotUsedForNullCheck, targetMethods) @@ -42,8 +41,11 @@ protected override void Analyze( if (!literalFirstArgument?.IsKind(SyntaxKind.NullLiteralExpression) ?? true) return; + var replacement = GetReplacementMethod(method.Name); + var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MethodName] = method.Name; + builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( @@ -56,9 +58,20 @@ protected override void Analyze( .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) - ) + ), + replacement ) ); } + + static string GetReplacementMethod(string methodName) + { + if (equalMethods.Contains(methodName)) + return Constants.Asserts.Null; + if (notEqualMethods.Contains(methodName)) + return Constants.Asserts.NotNull; + + return null; + } } } diff --git a/src/xunit.analyzers/AssertEqualsShouldNotBeUsed.cs b/src/xunit.analyzers/AssertEqualsShouldNotBeUsed.cs index 73c91f4c..3b51df4f 100644 --- a/src/xunit.analyzers/AssertEqualsShouldNotBeUsed.cs +++ b/src/xunit.analyzers/AssertEqualsShouldNotBeUsed.cs @@ -1,4 +1,5 @@ -using System.Collections.Immutable; +using System; +using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; @@ -24,8 +25,16 @@ protected override void Analyze( IInvocationOperation invocationOperation, IMethodSymbol method) { + var replacement = method.Name switch + { + nameof(object.Equals) => Constants.Asserts.Equal, + nameof(object.ReferenceEquals) => Constants.Asserts.Same, + _ => throw new InvalidOperationException($"Unexpected method name: {method.Name}") + }; + var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MethodName] = method.Name; + builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( @@ -37,7 +46,8 @@ protected override void Analyze( SymbolDisplayFormat .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) - ) + ), + replacement ) ); } diff --git a/src/xunit.analyzers/AssertRegexMatchShouldNotUseBoolLiteralCheck.cs b/src/xunit.analyzers/AssertRegexMatchShouldNotUseBoolLiteralCheck.cs index 8c296279..d20e5685 100644 --- a/src/xunit.analyzers/AssertRegexMatchShouldNotUseBoolLiteralCheck.cs +++ b/src/xunit.analyzers/AssertRegexMatchShouldNotUseBoolLiteralCheck.cs @@ -41,9 +41,15 @@ protected override void Analyze( if (!regexIsMatchSymbols.Contains(SymbolDisplay.ToDisplayString(methodSymbol))) return; + var replacement = + method.Name == Constants.Asserts.True + ? Constants.Asserts.Matches + : Constants.Asserts.DoesNotMatch; + var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MethodName] = method.Name; builder[Constants.Properties.IsStatic] = methodSymbol.IsStatic ? bool.TrueString : bool.FalseString; + builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( @@ -56,7 +62,8 @@ protected override void Analyze( .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) - ) + ), + replacement ) ); } diff --git a/src/xunit.analyzers/AssertSameShouldNotBeCalledOnValueTypes.cs b/src/xunit.analyzers/AssertSameShouldNotBeCalledOnValueTypes.cs index bde5b643..409b2fd8 100644 --- a/src/xunit.analyzers/AssertSameShouldNotBeCalledOnValueTypes.cs +++ b/src/xunit.analyzers/AssertSameShouldNotBeCalledOnValueTypes.cs @@ -36,9 +36,16 @@ protected override void Analyze( return; var typeToDisplay = firstArgumentType.IsReferenceType ? secondArgumentType : firstArgumentType; + var replacement = method.Name switch + { + Constants.Asserts.Same => Constants.Asserts.Equal, + Constants.Asserts.NotSame => Constants.Asserts.NotEqual, + _ => null, + }; var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MethodName] = method.Name; + builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( @@ -56,7 +63,8 @@ protected override void Analyze( SymbolDisplayFormat .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) - ) + ), + replacement ) ); } diff --git a/src/xunit.analyzers/AssertStringEqualityCheckShouldNotUseBoolCheck.cs b/src/xunit.analyzers/AssertStringEqualityCheckShouldNotUseBoolCheck.cs index b040ba66..767cba73 100644 --- a/src/xunit.analyzers/AssertStringEqualityCheckShouldNotUseBoolCheck.cs +++ b/src/xunit.analyzers/AssertStringEqualityCheckShouldNotUseBoolCheck.cs @@ -68,10 +68,16 @@ protected override void Analyze( ignoreCase = stringComparison == StringComparison.OrdinalIgnoreCase ? bool.TrueString : bool.FalseString; } + var replacement = + method.Name == Constants.Asserts.True + ? Constants.Asserts.Equal + : Constants.Asserts.NotEqual; + var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.AssertMethodName] = method.Name; builder[Constants.Properties.IsStaticMethodCall] = methodSymbol.IsStatic ? bool.TrueString : bool.FalseString; builder[Constants.Properties.IgnoreCase] = ignoreCase; + builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( @@ -84,7 +90,8 @@ protected override void Analyze( .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) - ) + ), + replacement ) ); } diff --git a/src/xunit.analyzers/AssertSubstringCheckShouldNotUseBoolCheck.cs b/src/xunit.analyzers/AssertSubstringCheckShouldNotUseBoolCheck.cs index ff20e454..b127ed88 100644 --- a/src/xunit.analyzers/AssertSubstringCheckShouldNotUseBoolCheck.cs +++ b/src/xunit.analyzers/AssertSubstringCheckShouldNotUseBoolCheck.cs @@ -37,7 +37,7 @@ protected override void Analyze( if (arguments.Length != 1) return; - if (!(arguments[0].Value is IInvocationOperation invocationExpression)) + if (arguments[0].Value is not IInvocationOperation invocationExpression) return; var methodSymbol = invocationExpression.TargetMethod; @@ -47,9 +47,12 @@ protected override void Analyze( if (methodSymbol.Name != Constants.Asserts.Contains && method.Name == Constants.Asserts.False) return; + var replacement = GetReplacementMethodName(method.Name, methodSymbol.Name); + var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.AssertMethodName] = method.Name; builder[Constants.Properties.SubstringMethodName] = methodSymbol.Name; + builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( @@ -62,9 +65,19 @@ protected override void Analyze( .CSharpShortErrorMessageFormat .WithParameterOptions(SymbolDisplayParameterOptions.None) .WithGenericsOptions(SymbolDisplayGenericsOptions.None) - ) + ), + replacement ) ); } + static string GetReplacementMethodName( + string assertMethodName, + string substringMethodName) + { + if (substringMethodName == nameof(string.Contains)) + return assertMethodName == Constants.Asserts.True ? Constants.Asserts.Contains : Constants.Asserts.DoesNotContain; + + return substringMethodName; + } } } diff --git a/src/xunit.analyzers/AssertThrowsShouldNotBeUsedForAsyncThrowsCheck.cs b/src/xunit.analyzers/AssertThrowsShouldNotBeUsedForAsyncThrowsCheck.cs index e923d2ea..9cd4f8dd 100644 --- a/src/xunit.analyzers/AssertThrowsShouldNotBeUsedForAsyncThrowsCheck.cs +++ b/src/xunit.analyzers/AssertThrowsShouldNotBeUsedForAsyncThrowsCheck.cs @@ -29,17 +29,25 @@ protected override void Analyze(OperationAnalysisContext context, IInvocationOpe if (!ThrowExpressionReturnsTask(throwExpressionSymbol, context)) return; + var replacement = method.Name + "Async"; + var builder = ImmutableDictionary.CreateBuilder(); builder[Constants.Properties.MethodName] = method.Name; + builder[Constants.Properties.Replacement] = replacement; context.ReportDiagnostic( Diagnostic.Create( - Descriptors.X2014_AssertThrowsShouldNotBeUsedForAsyncThrowsCheck, - invocationOperation.Syntax.GetLocation(), - builder.ToImmutable(), - SymbolDisplay.ToDisplayString( - method, - SymbolDisplayFormat.CSharpShortErrorMessageFormat.WithParameterOptions(SymbolDisplayParameterOptions.None).WithGenericsOptions(SymbolDisplayGenericsOptions.None)) + Descriptors.X2014_AssertThrowsShouldNotBeUsedForAsyncThrowsCheck, + invocationOperation.Syntax.GetLocation(), + builder.ToImmutable(), + SymbolDisplay.ToDisplayString( + method, + SymbolDisplayFormat + .CSharpShortErrorMessageFormat + .WithParameterOptions(SymbolDisplayParameterOptions.None) + .WithGenericsOptions(SymbolDisplayGenericsOptions.None) + ), + replacement ) ); } diff --git a/src/xunit.analyzers/AssertThrowsShouldUseGenericOverloadCheck.cs b/src/xunit.analyzers/AssertThrowsShouldUseGenericOverloadCheck.cs index f3c99298..8456887f 100644 --- a/src/xunit.analyzers/AssertThrowsShouldUseGenericOverloadCheck.cs +++ b/src/xunit.analyzers/AssertThrowsShouldUseGenericOverloadCheck.cs @@ -27,7 +27,7 @@ protected override void Analyze(OperationAnalysisContext context, IInvocationOpe return; var typeArgument = invocationOperation.Arguments.FirstOrDefault(arg => arg.Parameter.Equals(parameters[0]))?.Value; - if (!(typeArgument is ITypeOfOperation typeOfOperation)) + if (typeArgument is not ITypeOfOperation typeOfOperation) return; var type = typeOfOperation.TypeOperand; @@ -42,6 +42,7 @@ protected override void Analyze(OperationAnalysisContext context, IInvocationOpe Descriptors.X2015_AssertThrowsShouldUseGenericOverload, invocationOperation.Syntax.GetLocation(), builder.ToImmutable(), + method.Name, typeName ) ); diff --git a/src/xunit.analyzers/Utilities/Constants.cs b/src/xunit.analyzers/Utilities/Constants.cs index 80c9ce63..7700350d 100644 --- a/src/xunit.analyzers/Utilities/Constants.cs +++ b/src/xunit.analyzers/Utilities/Constants.cs @@ -22,6 +22,7 @@ public static class Asserts public const string DoesNotContain = "DoesNotContain"; public const string DoesNotMatch = "DoesNotMatch"; public const string Empty = "Empty"; + public const string EndsWith = "EndsWith"; public const string Equal = "Equal"; public const string False = "False"; public const string IsAssignableFrom = "IsAssignableFrom"; @@ -36,9 +37,11 @@ public static class Asserts public const string Null = "Null"; public const string Same = "Same"; public const string Single = "Single"; + public const string StartsWith = "StartsWith"; public const string StrictEqual = "StrictEqual"; public const string Throws = "Throws"; public const string ThrowsAny = "ThrowsAny"; + public const string ThrowsAnyAsync = "ThrowsAnyAsync"; public const string ThrowsAsync = "ThrowsAsync"; public const string True = "True"; } @@ -77,6 +80,7 @@ public static class Properties public const string ParameterIndex = "ParameterIndex"; public const string ParameterName = "ParameterName"; public const string ParameterSpecialType = "ParameterSpecialType"; + public const string Replacement = "Replacement"; public const string SizeValue = "SizeValue"; public const string SubstringMethodName = "SubstringMethodName"; public const string TestClassName = "TestClassName"; diff --git a/src/xunit.analyzers/Utilities/Descriptors.cs b/src/xunit.analyzers/Utilities/Descriptors.cs index 7d349e20..db9e7739 100644 --- a/src/xunit.analyzers/Utilities/Descriptors.cs +++ b/src/xunit.analyzers/Utilities/Descriptors.cs @@ -20,20 +20,19 @@ public enum Category public static class Descriptors { - static ConcurrentDictionary categoryMapping = new(); + static readonly ConcurrentDictionary categoryMapping = new(); static DiagnosticDescriptor Rule( string id, string title, Category category, DiagnosticSeverity defaultSeverity, - string messageFormat, - string description = null) + string messageFormat) { var helpLink = $"https://xunit.net/xunit.analyzers/rules/{id}"; - var isEnabledByDefault = true; + var categoryString = categoryMapping.GetOrAdd(category, c => c.ToString()); - return new DiagnosticDescriptor(id, title, messageFormat, categoryMapping.GetOrAdd(category, c => c.ToString()), defaultSeverity, isEnabledByDefault, description, helpLink); + return new DiagnosticDescriptor(id, title, messageFormat, categoryString, defaultSeverity, isEnabledByDefault: true, helpLinkUri: helpLink); } public static DiagnosticDescriptor X1000_TestClassMustBePublic { get; } = @@ -42,7 +41,7 @@ static DiagnosticDescriptor Rule( "Test classes must be public", Usage, Error, - "Test classes must be public" + "Test classes must be public. Add or change the visibility modifier of the test class to public." ); public static DiagnosticDescriptor X1001_FactMethodMustNotHaveParameters { get; } = @@ -51,8 +50,7 @@ static DiagnosticDescriptor Rule( "Fact methods cannot have parameters", Usage, Error, - "Fact methods cannot have parameters", - "Remove the parameters from the method or convert it into a Theory." + "Fact methods cannot have parameters. Remove the parameters from the method or convert it into a Theory." ); public static DiagnosticDescriptor X1002_TestMethodMustNotHaveMultipleFactAttributes { get; } = @@ -61,7 +59,7 @@ static DiagnosticDescriptor Rule( "Test methods cannot have multiple Fact or Theory attributes", Usage, Error, - "Test methods cannot have multiple Fact or Theory attributes" + "Test methods cannot have multiple Fact or Theory attributes. Remove all but one of the attributes." ); public static DiagnosticDescriptor X1003_TheoryMethodMustHaveTestData { get; } = @@ -70,8 +68,7 @@ static DiagnosticDescriptor Rule( "Theory methods must have test data", Usage, Error, - "Theory methods must have test data", - "Use InlineData, MemberData, or ClassData to provide test data for the Theory" + "Theory methods must have test data. Use InlineData, MemberData, or ClassData to provide test data for the Theory." ); public static DiagnosticDescriptor X1004_TestMethodShouldNotBeSkipped { get; } = @@ -80,7 +77,7 @@ static DiagnosticDescriptor Rule( "Test methods should not be skipped", Usage, Info, - "Test methods should not be skipped" + "Test methods should not be skipped. Remove the Skip property to start running the test again." ); public static DiagnosticDescriptor X1005_FactMethodShouldNotHaveTestData { get; } = @@ -89,7 +86,7 @@ static DiagnosticDescriptor Rule( "Fact methods should not have test data", Usage, Warning, - "Fact methods should not have test data" + "Fact methods should not have test data. Remove the test data, or convert the Fact to a Theory." ); public static DiagnosticDescriptor X1006_TheoryMethodShouldHaveParameters { get; } = @@ -98,7 +95,7 @@ static DiagnosticDescriptor Rule( "Theory methods should have parameters", Usage, Warning, - "Theory methods should have parameters" + "Theory methods should have parameters. Add parameter(s) to the theory method." ); public static DiagnosticDescriptor X1007_ClassDataAttributeMustPointAtValidClass { get; } = @@ -107,8 +104,7 @@ static DiagnosticDescriptor Rule( "ClassData must point at a valid class", Usage, Error, - "ClassData must point at a valid class", - "The class {0} must be public, not sealed, with an empty constructor, and implement IEnumerable." + "ClassData must point at a valid class. The class {0} must be public, not sealed, with an empty constructor, and implement IEnumerable." ); public static DiagnosticDescriptor X1008_DataAttributeShouldBeUsedOnATheory { get; } = @@ -117,7 +113,7 @@ static DiagnosticDescriptor Rule( "Test data attribute should only be used on a Theory", Usage, Warning, - "Test data attribute should only be used on a Theory" + "Test data attribute should only be used on a Theory. Remove the test data, or add the Theory attribute to the test method." ); public static DiagnosticDescriptor X1009_InlineDataMustMatchTheoryParameters_TooFewValues { get; } = @@ -126,7 +122,7 @@ static DiagnosticDescriptor Rule( "InlineData values must match the number of method parameters", Usage, Error, - "InlineData values must match the number of method parameters" + "InlineData values must match the number of method parameters. Remove unused parameters, or add more data for the missing parameters." ); public static DiagnosticDescriptor X1010_InlineDataMustMatchTheoryParameters_IncompatibleValueType { get; } = @@ -135,7 +131,7 @@ static DiagnosticDescriptor Rule( "The value is not convertible to the method parameter type", Usage, Error, - "The value is not convertible to the method parameter '{0}' of type '{1}'." + "The value is not convertible to the method parameter '{0}' of type '{1}'. Use a compatible data value." ); public static DiagnosticDescriptor X1011_InlineDataMustMatchTheoryParameters_ExtraValue { get; } = @@ -144,7 +140,7 @@ static DiagnosticDescriptor Rule( "There is no matching method parameter", Usage, Error, - "There is no matching method parameter for value: {0}." + "There is no matching method parameter for value: {0}. Remove unused value(s), or add more parameter(s)." ); public static DiagnosticDescriptor X1012_InlineDataMustMatchTheoryParameters_NullShouldNotBeUsedForIncompatibleParameter { get; } = @@ -153,8 +149,7 @@ static DiagnosticDescriptor Rule( "Null should not be used for value type parameters", Usage, Warning, - "Null should not be used for value type parameter '{0}' of type '{1}'.", - "xUnit.net will execute the theory initializing the parameter to the default value of the type, which might not be the desired behavior" + "Null should not be used for value type parameter '{0}' of type '{1}'. Use a non-null value, or convert the parameter to a nullable type." ); public static DiagnosticDescriptor X1013_PublicMethodShouldBeMarkedAsTest { get; } = @@ -163,9 +158,7 @@ static DiagnosticDescriptor Rule( "Public method should be marked as test", Usage, Warning, - "Public method '{0}' on test class '{1}' should be marked as a {2}.", - "Public methods on a test class that return void or Task should be marked as tests or have their accessibility reduced. While test methods do not have to be public, " - + "having public non-test methods might indicate that a method was intended to be a test but the annotation was not applied." + "Public method '{0}' on test class '{1}' should be marked as a {2}. Reduce the visibility of the method, or add a {2} attribute to the method." ); public static DiagnosticDescriptor X1014_MemberDataShouldUseNameOfOperator { get; } = @@ -174,7 +167,7 @@ static DiagnosticDescriptor Rule( "MemberData should use nameof operator for member name", Usage, Warning, - "MemberData should use nameof operator to reference member '{0}' on type '{1}'." + "MemberData should use nameof operator to reference member '{0}' on type '{1}'. Replace the constant string with nameof." ); public static DiagnosticDescriptor X1015_MemberDataMustReferenceExistingMember { get; } = @@ -183,7 +176,7 @@ static DiagnosticDescriptor Rule( "MemberData must reference an existing member", Usage, Error, - "MemberData must reference an existing member '{0}' on type '{1}'." + "MemberData must reference an existing member '{0}' on type '{1}'. Fix the member reference, or add the missing data member." ); public static DiagnosticDescriptor X1016_MemberDataMustReferencePublicMember { get; } = @@ -192,7 +185,7 @@ static DiagnosticDescriptor Rule( "MemberData must reference a public member", Usage, Error, - "MemberData must reference a public member" + "MemberData must reference a public member. Add or change the visibility of the data member to public." ); public static DiagnosticDescriptor X1017_MemberDataMustReferenceStaticMember { get; } = @@ -201,7 +194,7 @@ static DiagnosticDescriptor Rule( "MemberData must reference a static member", Usage, Error, - "MemberData must reference a static member" + "MemberData must reference a static member. Add the static modifier to the data member." ); public static DiagnosticDescriptor X1018_MemberDataMustReferenceValidMemberKind { get; } = @@ -210,7 +203,7 @@ static DiagnosticDescriptor Rule( "MemberData must reference a valid member kind", Usage, Error, - "MemberData must reference a property, field, or method" + "MemberData must reference a property, field, or method. Convert the data member to a compatible member type." ); public static DiagnosticDescriptor X1019_MemberDataMustReferenceMemberOfValidType { get; } = @@ -237,8 +230,7 @@ static DiagnosticDescriptor Rule( "MemberData should not have parameters if the referenced member is not a method", Usage, Warning, - "MemberData should not have parameters if the referenced member is not a method", - "Additional MemberData parameters are only used for methods. They are ignored for fields and properties." + "MemberData should not have parameters if the referenced member is not a method. Remove the parameter values, or convert the data member to a method with parameters." ); public static DiagnosticDescriptor X1022_TheoryMethodCannotHaveParameterArray { get; } = @@ -247,8 +239,7 @@ static DiagnosticDescriptor Rule( "Theory methods cannot have a parameter array", Usage, Error, - "Theory method '{0}' on test class '{1}' cannot have a parameter array '{2}'.", - "Params array support was added in xUnit.net 2.2. Remove the parameter or upgrade the xUnit.net binaries." + "Theory method '{0}' on test class '{1}' cannot have a parameter array '{2}'. Upgrade to xUnit.net 2.2 or later to enable this feature." ); public static DiagnosticDescriptor X1023_TheoryMethodCannotHaveDefaultParameter { get; } = @@ -257,8 +248,7 @@ static DiagnosticDescriptor Rule( "Theory methods cannot have default parameter values", Usage, Error, - "Theory method '{0}' on test class '{1}' parameter '{2}' cannot have a default value.", - "Default parameter values support was added in xUnit.net 2.2. Remove the default value or upgrade the xUnit.net binaries." + "Theory method '{0}' on test class '{1}' parameter '{2}' cannot have a default value. Upgrade to xUnit.net 2.2 or later to enable this feature." ); public static DiagnosticDescriptor X1024_TestMethodCannotHaveOverloads { get; } = @@ -267,10 +257,7 @@ static DiagnosticDescriptor Rule( "Test methods cannot have overloads", Usage, Error, - "Test method '{0}' on test class '{1}' has the same name as another method declared on class '{2}'.", - "Test method overloads are not supported as most test runners cannot correctly invoke the appropriate overload. " - + "This includes any combination of static and instance methods declared with any visibility in the same class or across a " - + "class hierarchy. Rename one of the methods." + "Test method '{0}' on test class '{1}' has the same name as another method declared on class '{2}'. Rename method(s) so that there are no overloaded names." ); public static DiagnosticDescriptor X1025_InlineDataShouldBeUniqueWithinTheory { get; } = @@ -279,8 +266,7 @@ static DiagnosticDescriptor Rule( "InlineData should be unique within the Theory it belongs to", Usage, Warning, - "Theory method '{0}' on test class '{1}' has InlineData duplicate(s).", - "Theory should have all InlineData elements unique. Remove redundant attribute(s) from the theory method." + "Theory method '{0}' on test class '{1}' has InlineData duplicate(s). Remove redundant attribute(s) from the theory method." ); public static DiagnosticDescriptor X1026_TheoryMethodShouldUseAllParameters { get; } = @@ -289,7 +275,7 @@ static DiagnosticDescriptor Rule( "Theory methods should use all of their parameters", Usage, Warning, - "Theory method '{0}' on test class '{1}' does not use parameter '{2}'." + "Theory method '{0}' on test class '{1}' does not use parameter '{2}'. Use the parameter, or remove the parameter and associated data." ); public static DiagnosticDescriptor X1027_CollectionDefinitionClassMustBePublic { get; } = @@ -298,7 +284,7 @@ static DiagnosticDescriptor Rule( "Collection definition classes must be public", Usage, Error, - "Collection definition classes must be public" + "Collection definition classes must be public. Add or change the visibility modifier of the collection definition class to public." ); // Placeholder for rule X1028 @@ -331,7 +317,7 @@ static DiagnosticDescriptor Rule( "Test classes decorated with 'Xunit.IClassFixture' or 'Xunit.ICollectionFixture' should add a constructor argument of type TFixture", Usage, Info, - "Test class '{0}' does not contain constructor argument of type '{1}'." + "Test class '{0}' does not contain constructor argument of type '{1}'. Add a constructor argument to consume the fixture data." ); public static DiagnosticDescriptor X2000_AssertEqualLiteralValueShouldBeFirst { get; } = @@ -340,8 +326,7 @@ static DiagnosticDescriptor Rule( "Constants and literals should be the expected argument", Assertions, Warning, - "The literal or constant value {0} should be passed as the 'expected' argument in the call to '{1}' in method '{2}' on type '{3}'.", - "The xUnit.net Assertion library produces the best error messages if the expected value is passed in as the expected argument." + "The literal or constant value {0} should be passed as the 'expected' argument in the call to '{1}' in method '{2}' on type '{3}'. Swap the parameter values." ); public static DiagnosticDescriptor X2001_AssertEqualsShouldNotBeUsed { get; } = @@ -350,7 +335,7 @@ static DiagnosticDescriptor Rule( "Do not use invalid equality check", Assertions, Hidden, - "Do not use {0}." + "Do not use {0}. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2002_AssertNullShouldNotBeCalledOnValueTypes { get; } = @@ -359,7 +344,7 @@ static DiagnosticDescriptor Rule( "Do not use null check on value type", Assertions, Warning, - "Do not use {0} on value type '{1}'." + "Do not use {0} on value type '{1}'. Remove this assert." ); public static DiagnosticDescriptor X2003_AssertEqualShouldNotUsedForNullCheck { get; } = @@ -368,7 +353,7 @@ static DiagnosticDescriptor Rule( "Do not use equality check to test for null value", Assertions, Warning, - "Do not use {0} to check for null value." + "Do not use {0} to check for null value. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2004_AssertEqualShouldNotUsedForBoolLiteralCheck { get; } = @@ -377,7 +362,7 @@ static DiagnosticDescriptor Rule( "Do not use equality check to test for boolean conditions", Assertions, Warning, - "Do not use {0} to check for boolean conditions." + "Do not use {0} to check for boolean conditions. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2005_AssertSameShouldNotBeCalledOnValueTypes { get; } = @@ -386,8 +371,7 @@ static DiagnosticDescriptor Rule( "Do not use identity check on value type", Assertions, Warning, - "Do not use {0} on value type '{1}'.", - "The value type will be boxed which means its identity will always be different." + "Do not use {0} on value type '{1}'. Value types do not have identity. Use Assert.{2} instead." ); public static DiagnosticDescriptor X2006_AssertEqualGenericShouldNotBeUsedForStringValue { get; } = @@ -396,7 +380,7 @@ static DiagnosticDescriptor Rule( "Do not use invalid string equality check", Assertions, Warning, - "Do not use {0} to test for string equality." + "Do not use {0} to test for string equality. Use {1} instead." ); public static DiagnosticDescriptor X2007_AssertIsTypeShouldUseGenericOverload { get; } = @@ -405,7 +389,7 @@ static DiagnosticDescriptor Rule( "Do not use typeof expression to check the type", Assertions, Warning, - "Do not use typeof({0}) expression to check the type." + "Do not use typeof({0}) expression to check the type. Use Assert.IsType<{0}> instead." ); public static DiagnosticDescriptor X2008_AssertRegexMatchShouldNotUseBoolLiteralCheck { get; } = @@ -414,7 +398,7 @@ static DiagnosticDescriptor Rule( "Do not use boolean check to match on regular expressions", Assertions, Warning, - "Do not use {0} to match on regular expressions." + "Do not use {0} to match on regular expressions. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2009_AssertSubstringCheckShouldNotUseBoolCheck { get; } = @@ -423,7 +407,7 @@ static DiagnosticDescriptor Rule( "Do not use boolean check to check for substrings", Assertions, Warning, - "Do not use {0} to check for substrings." + "Do not use {0} to check for substrings. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2010_AssertStringEqualityCheckShouldNotUseBoolCheckFixer { get; } = @@ -432,7 +416,7 @@ static DiagnosticDescriptor Rule( "Do not use boolean check to check for string equality", Assertions, Warning, - "Do not use {0} to check for string equality." + "Do not use {0} to check for string equality. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2011_AssertEmptyCollectionCheckShouldNotBeUsed { get; } = @@ -441,16 +425,16 @@ static DiagnosticDescriptor Rule( "Do not use empty collection check", Assertions, Warning, - "Do not use {0} to check for empty collections." + "Do not use {0} to check for empty collections. Add element inspectors (for non-empty collections), or use Assert.Empty (for empty collections) instead." ); public static DiagnosticDescriptor X2012_AssertEnumerableAnyCheckShouldNotBeUsedForCollectionContainsCheck { get; } = Rule( "xUnit2012", - "Do not use Enumerable.Any() to check if a value exists in a collection", + "Do not use boolean check to check if a value exists in a collection", Assertions, Warning, - "Do not use Enumerable.Any() to check if a value exists in a collection." + "Do not use {0} to check if a value exists in a collection. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2013_AssertEqualShouldNotBeUsedForCollectionSizeCheck { get; } = @@ -459,7 +443,7 @@ static DiagnosticDescriptor Rule( "Do not use equality check to check for collection size.", Assertions, Warning, - "Do not use {0} to check for collection size." + "Do not use {0} to check for collection size. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2014_AssertThrowsShouldNotBeUsedForAsyncThrowsCheck { get; } = @@ -468,7 +452,7 @@ static DiagnosticDescriptor Rule( "Do not use throws check to check for asynchronously thrown exception", Assertions, Error, - "Do not use {0} to check for asynchronously thrown exceptions." + "Do not use {0} to check for asynchronously thrown exceptions. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2015_AssertThrowsShouldUseGenericOverload { get; } = @@ -477,7 +461,7 @@ static DiagnosticDescriptor Rule( "Do not use typeof expression to check the exception type", Assertions, Warning, - "Do not use typeof() expression to check the exception type." + "Do not use typeof({1}) expression to check the exception type. Use Assert.{0}<{1}> instead." ); public static DiagnosticDescriptor X2016_AssertEqualPrecisionShouldBeInRange { get; } = @@ -495,7 +479,7 @@ static DiagnosticDescriptor Rule( "Do not use Contains() to check if a value exists in a collection", Assertions, Warning, - "Do not use Contains() to check if a value exists in a collection." + "Do not use {0} to check if a value exists in a collection. Use Assert.{1} instead." ); public static DiagnosticDescriptor X2018_AssertIsTypeShouldNotBeUsedForAbstractType { get; } = @@ -504,18 +488,12 @@ static DiagnosticDescriptor Rule( "Do not compare an object's exact type to an abstract class or interface", Assertions, Warning, - "Do not compare an object's exact type to the {0} '{1}'." + "Do not compare an object's exact type to the {0} '{1}'. Use Assert.IsAssignableFrom instead." ); [Obsolete("This check was unnecessary, as it's already covered by xUnit2014", error: true)] - public static DiagnosticDescriptor X2019_AssertThrowsShouldNotBeUsedForAsyncThrowsCheck { get; } = - Rule( - "xUnit2019", - "Do not use obsolete throws check to check for asynchronously thrown exception", - Assertions, - Hidden, - "Do not use obsolete {0} to check for asynchronously thrown exceptions." - ); + public static DiagnosticDescriptor X2019_AssertThrowsShouldNotBeUsedForAsyncThrowsCheck + => throw new NotImplementedException(); // Placeholder for rule X2020 @@ -543,7 +521,7 @@ static DiagnosticDescriptor Rule( "Test case classes must derive directly or indirectly from Xunit.LongLivedMarshalByRefObject", Extensibility, Error, - "Test case class {0} must derive directly or indirectly from Xunit.LongLivedMarshalByRefObject" + "Test case class {0} must derive directly or indirectly from Xunit.LongLivedMarshalByRefObject." ); public static DiagnosticDescriptor X3001_SerializableClassMustHaveParameterlessConstructor { get; } = @@ -552,7 +530,7 @@ static DiagnosticDescriptor Rule( "Classes that implement Xunit.Abstractions.IXunitSerializable must have a public parameterless constructor", Extensibility, Error, - "Class {0} must have a public parameterless constructor to support Xunit.Abstractions.IXunitSerializable" + "Class {0} must have a public parameterless constructor to support Xunit.Abstractions.IXunitSerializable." ); // Placeholder for rule X3002