From d14cc5754351027ed1244dad181c3767dddbaced Mon Sep 17 00:00:00 2001 From: Jihoon Park Date: Fri, 12 Apr 2024 19:44:49 -0400 Subject: [PATCH] Fix logic around out-of-order arguments for classic assertions (#712) (#716) * Fix #712: AreEqualClassicModelAssertUsageCodeFix no longer assumes that the first argument is expected and the second argument is actual * Add a new overload for UpdateArguments * Make all code functional but not yet readable/maintainable * Refactoring * Handle out-of-order arguments in all ClassicModelAssertUsageCodeFixes * Add out-of-order test cases to CollectionAssertUsageCodeFixTests * Update all other CodeFixTests, fixing CodeFixes to account for bugs found * Remove an extra whitespace * Code review changes * Omit named parameters for the constraint model * Add test case: two arguments for params in all ClassicAssert tests * Omit named parameters in StringAssertUsageCodeFix * Consistently use WithNameColon Update Tests to remove named parameters * Code review changes * We no longer need HasToleranceValue * We no longer need ComparerParameterIndex * Update param tags for GetInterpolatedMessageArgumentOrDefault --------- Co-authored-by: Manfred Brands --- ...qualClassicModelAssertUsageCodeFixTests.cs | 138 ++++++++++++++---- ...qualClassicModelAssertUsageCodeFixTests.cs | 54 +++++-- ...SameClassicModelAssertUsageCodeFixTests.cs | 66 +++++++-- ...SameClassicModelAssertUsageCodeFixTests.cs | 66 +++++++-- .../ClassicModelAssertUsageCodeFixTests.cs | 8 +- ...ainsClassicModelAssertUsageCodeFixTests.cs | 66 +++++++-- ...aterClassicModelAssertUsageCodeFixTests.cs | 94 ++++++++++-- ...qualClassicModelAssertUsageCodeFixTests.cs | 94 ++++++++++-- ...mptyClassicModelAssertUsageCodeFixTests.cs | 62 ++++++-- ...alseClassicModelAssertUsageCodeFixTests.cs | 40 ++++- ...ceOfClassicModelAssertUsageCodeFixTests.cs | 112 +++++++++++--- ...sNaNClassicModelAssertUsageCodeFixTests.cs | 62 ++++++-- ...mptyClassicModelAssertUsageCodeFixTests.cs | 98 +++++++++++-- ...ceOfClassicModelAssertUsageCodeFixTests.cs | 112 +++++++++++--- ...NullClassicModelAssertUsageCodeFixTests.cs | 44 +++++- ...NullClassicModelAssertUsageCodeFixTests.cs | 44 +++++- ...TrueClassicModelAssertUsageCodeFixTests.cs | 79 +++++++++- ...icModelAssertUsageCondensedCodeFixTests.cs | 42 +++++- ...LessClassicModelAssertUsageCodeFixTests.cs | 94 ++++++++++-- ...qualClassicModelAssertUsageCodeFixTests.cs | 94 ++++++++++-- ...ZeroClassicModelAssertUsageCodeFixTests.cs | 62 ++++++-- ...ZeroClassicModelAssertUsageCodeFixTests.cs | 62 ++++++-- .../CollectionAssertUsageAnalyzerTests.cs | 43 +++++- .../CollectionAssertUsageCodeFixTests.cs | 124 +++++++++++++--- .../StringAssertUsageCodeFixTests.cs | 15 +- .../AreEqualClassicModelAssertUsageCodeFix.cs | 28 ++-- ...eNotEqualClassicModelAssertUsageCodeFix.cs | 13 +- ...reNotSameClassicModelAssertUsageCodeFix.cs | 13 +- .../AreSameClassicModelAssertUsageCodeFix.cs | 13 +- .../ClassicModelAssertUsageAnalyzer.cs | 4 - .../ClassicModelAssertUsageCodeFix.cs | 98 +++++++++---- .../ContainsClassicModelAssertUsageCodeFix.cs | 13 +- .../GreaterClassicModelAssertUsageCodeFix.cs | 13 +- ...erOrEqualClassicModelAssertUsageCodeFix.cs | 13 +- .../IsEmptyClassicModelAssertUsageCodeFix.cs | 15 +- ...eAndFalseClassicModelAssertUsageCodeFix.cs | 10 +- ...nstanceOfClassicModelAssertUsageCodeFix.cs | 24 ++- .../IsNaNClassicModelAssertUsageCodeFix.cs | 10 +- ...sNotEmptyClassicModelAssertUsageCodeFix.cs | 14 +- ...nstanceOfClassicModelAssertUsageCodeFix.cs | 24 ++- ...ndNotNullClassicModelAssertUsageCodeFix.cs | 11 +- ...llAndNullClassicModelAssertUsageCodeFix.cs | 11 +- ...ueAndTrueClassicModelAssertUsageCodeFix.cs | 11 +- ...ClassicModelAssertUsageCondensedCodeFix.cs | 7 +- .../LessClassicModelAssertUsageCodeFix.cs | 13 +- ...ssOrEqualClassicModelAssertUsageCodeFix.cs | 13 +- .../NotZeroClassicModelAssertUsageCodeFix.cs | 10 +- .../ZeroClassicModelAssertUsageCodeFix.cs | 10 +- .../CollectionAssertUsageAnalyzer.cs | 10 -- .../CollectionAssertUsageCodeFix.cs | 115 ++++++++++----- .../Constants/AnalyzerPropertyKeys.cs | 2 - .../Constants/NUnitFrameworkConstants.cs | 14 ++ src/nunit.analyzers/Helpers/CodeFixHelper.cs | 34 +++++ .../StringAssertUsageCodeFix.cs | 33 ++++- ...ingFormatToInterpolatableStringAnalyzer.cs | 2 +- 55 files changed, 1931 insertions(+), 445 deletions(-) diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFixTests.cs index e1ddfdc6..b3fb803d 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFixTests.cs @@ -27,11 +27,43 @@ public void VerifyGetFixableDiagnosticIds() [Test] public void VerifyAreEqualFix() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { ↓ClassicAssert.AreEqual(2d, 3d); - }}"); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + Assert.That(3d, Is.EqualTo(2d)); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyAreEqualFixWhenToleranceExistsWithNamedArgumentButInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + ↓ClassicAssert.AreEqual(delta: 0.0000001d, actual: 3d, expected: 2d); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + Assert.That(3d, Is.EqualTo(2d).Within(0.0000001d)); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyAreEqualFixWithNamedParametersInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + ↓ClassicAssert.AreEqual(actual: 3d, expected: 2d); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -43,11 +75,11 @@ public void TestMethod() [Test] public void VerifyAreEqualFixWithMessage() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { ↓ClassicAssert.AreEqual(2d, 3d, ""message""); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -73,13 +105,13 @@ public void TestMethod() } [Test] - public void VerifyAreEqualFixWithMessageAndParams() + public void VerifyAreEqualFixWithMessageAndOneArgumentForParams() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ - ↓ClassicAssert.AreEqual(2d, 3d, ""message-id: {{0}}"", Guid.NewGuid()); - }}"); + { + ↓ClassicAssert.AreEqual(2d, 3d, ""message-id: {0}"", Guid.NewGuid()); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -88,14 +120,50 @@ public void TestMethod() RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + [Test] + public void VerifyAreEqualFixWithMessageAnd2ParamsInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var actual = 3d; + ↓ClassicAssert.AreEqual(delta: 0.0000001d, expected: 2d, actual: actual, args: new[] { ""Guid.NewGuid()"", Guid.NewGuid().ToString() }, message: ""message-id: {0}, {1}""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var actual = 3d; + Assert.That(actual, Is.EqualTo(2d).Within(0.0000001d), $""message-id: {""Guid.NewGuid()""}, {Guid.NewGuid().ToString()}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyAreEqualFixWithMessageAnd2ParamsInStandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var actual = 3d; + ↓ClassicAssert.AreEqual(expected: 2d, actual: actual, delta: 0.0000001d, ""message-id: {0}, {1}"", ""Guid.NewGuid()"", Guid.NewGuid()); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var actual = 3d; + Assert.That(actual, Is.EqualTo(2d).Within(0.0000001d), $""message-id: {""Guid.NewGuid()""}, {Guid.NewGuid()}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + [Test] public void VerifyAreEqualFixWhenToleranceExists() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { ↓ClassicAssert.AreEqual(2d, 3d, 0.0000001d); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -107,15 +175,15 @@ public void TestMethod() [Test] public void VerifyAreEqualFixWhenToleranceExistsWithNamedArgument() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { ↓ClassicAssert.AreEqual(expected: 2d, actual: 3d, delta: 0.0000001d); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { - Assert.That(actual: 3d, Is.EqualTo(expected: 2d).Within(0.0000001d)); + Assert.That(3d, Is.EqualTo(2d).Within(0.0000001d)); }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } @@ -123,11 +191,11 @@ public void TestMethod() [Test] public void VerifyAreEqualFixWhenToleranceExistsWithMessage() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { ↓ClassicAssert.AreEqual(2d, 3d, 0.0000001d, ""message""); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -153,13 +221,13 @@ public void TestMethod() } [Test] - public void VerifyAreEqualFixWhenToleranceExistsWithMessageAndParams() + public void VerifyAreEqualFixWhenToleranceExistsWithMessageAndOneArgumentForParams() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ - ↓ClassicAssert.AreEqual(2d, 3d, 0.0000001d, ""message-id: {{0}}"", Guid.NewGuid()); - }}"); + { + ↓ClassicAssert.AreEqual(2d, 3d, 0.0000001d, ""message-id: {0}"", Guid.NewGuid()); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -168,10 +236,26 @@ public void TestMethod() RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + [Test] + public void VerifyAreEqualFixWhenToleranceExistsWithMessageAndTwoArgumentsForParams() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + ↓ClassicAssert.AreEqual(2d, 3d, 0.0000001d, ""{0}, {1}"", ""first"", ""second""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + Assert.That(3d, Is.EqualTo(2d).Within(0.0000001d), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + [Test] public void CodeFixPreservesLineBreakBeforeMessage() { - var code = TestUtility.WrapInTestMethod($@" + var code = TestUtility.WrapInTestMethod(@" ClassicAssert.AreEqual(2d, 3d, 0.0000001d, ""message"");"); diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreNotEqualClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreNotEqualClassicModelAssertUsageCodeFixTests.cs index b7d21257..e49697fa 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreNotEqualClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreNotEqualClassicModelAssertUsageCodeFixTests.cs @@ -27,11 +27,11 @@ public void VerifyGetFixableDiagnosticIds() [Test] public void VerifyAreNotEqualFix() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { ↓ClassicAssert.AreNotEqual(2d, 3d); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -43,11 +43,11 @@ public void TestMethod() [Test] public void VerifyAreNotEqualFixWithMessage() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { ↓ClassicAssert.AreNotEqual(2d, 3d, ""message""); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -57,13 +57,13 @@ public void TestMethod() } [Test] - public void VerifyAreNotEqualFixWithMessageAndParams() + public void VerifyAreNotEqualFixWithMessageAndOneArgumentForParams() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ - ↓ClassicAssert.AreNotEqual(2d, 3d, ""message-id: {{0}}"", Guid.NewGuid()); - }}"); + { + ↓ClassicAssert.AreNotEqual(2d, 3d, ""message-id: {0}"", Guid.NewGuid()); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -71,5 +71,37 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void VerifyAreNotEqualFixWithMessageAndTwoArgumentsForParams() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + ↓ClassicAssert.AreNotEqual(2d, 3d, ""{0}, {1}"", ""first"", ""second""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + Assert.That(3d, Is.Not.EqualTo(2d), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyAreNotEqualFixWithMessageAndArrayParamsInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + ↓ClassicAssert.AreNotEqual(args: new[] { ""first"", ""second"" }, actual: ""actual"", message: ""{0}, {1}"", expected: ""expected""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + Assert.That(""actual"", Is.Not.EqualTo(""expected""), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreNotSameClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreNotSameClassicModelAssertUsageCodeFixTests.cs index 62bf0e68..25e26060 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreNotSameClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreNotSameClassicModelAssertUsageCodeFixTests.cs @@ -27,14 +27,14 @@ public void VerifyGetFixableDiagnosticIds() [Test] public void VerifyAreNotSameFix() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expected = new object(); var actual = new object(); ↓ClassicAssert.AreNotSame(expected, actual); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -49,14 +49,14 @@ public void TestMethod() [Test] public void VerifyAreNotSameFixWithMessage() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expected = new object(); var actual = new object(); ↓ClassicAssert.AreNotSame(expected, actual, ""message""); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -69,16 +69,16 @@ public void TestMethod() } [Test] - public void VerifyAreNotSameFixWithMessageAndParams() + public void VerifyAreNotSameFixWithMessageAndOneArgumentForParams() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expected = new object(); var actual = new object(); - ↓ClassicAssert.AreNotSame(expected, actual, ""message-id: {{0}}"", Guid.NewGuid()); - }}"); + ↓ClassicAssert.AreNotSame(expected, actual, ""message-id: {0}"", Guid.NewGuid()); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -89,5 +89,49 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void VerifyAreNotSameFixWithMessageAndTwoArgumentsForParams() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = new object(); + var actual = new object(); + + ↓ClassicAssert.AreNotSame(expected, actual, ""{0}, {1}"", ""first"", ""second""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = new object(); + var actual = new object(); + + Assert.That(actual, Is.Not.SameAs(expected), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyAreNotSameFixWithMessageAndArrayParamsInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = new object(); + var actual = new object(); + + ↓ClassicAssert.AreNotSame(args: new[] { ""first"", ""second"" }, actual: actual, message: ""{0}, {1}"", expected: expected); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = new object(); + var actual = new object(); + + Assert.That(actual, Is.Not.SameAs(expected), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreSameClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreSameClassicModelAssertUsageCodeFixTests.cs index cd6b5a38..ca0dc975 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreSameClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreSameClassicModelAssertUsageCodeFixTests.cs @@ -27,14 +27,14 @@ public void VerifyGetFixableDiagnosticIds() [Test] public void VerifyAreSameFix() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expected = new object(); var actual = new object(); ↓ClassicAssert.AreSame(expected, actual); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -49,14 +49,14 @@ public void TestMethod() [Test] public void VerifyAreSameFixWithMessage() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expected = new object(); var actual = new object(); ↓ClassicAssert.AreSame(expected, actual, ""message""); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -69,16 +69,16 @@ public void TestMethod() } [Test] - public void VerifyAreSameFixWithMessageAndParams() + public void VerifyAreSameFixWithMessageAndOneArgumentForParams() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expected = new object(); var actual = new object(); - ↓ClassicAssert.AreSame(expected, actual, ""message-id: {{0}}"", Guid.NewGuid()); - }}"); + ↓ClassicAssert.AreSame(expected, actual, ""message-id: {0}"", Guid.NewGuid()); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -89,5 +89,49 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void VerifyAreSameFixWithMessageAndTwoArgumentsForParams() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = new object(); + var actual = new object(); + + ↓ClassicAssert.AreSame(expected, actual, ""{0}, {1}"", ""first"", ""second""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = new object(); + var actual = new object(); + + Assert.That(actual, Is.SameAs(expected), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyAreSameFixWithMessageAndArrayParamsInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = new object(); + var actual = new object(); + + ↓ClassicAssert.AreSame(args: new[] { ""first"", ""second"" }, actual: actual, message: ""{0}, {1}"", expected: expected); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = new object(); + var actual = new object(); + + Assert.That(actual, Is.SameAs(expected), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/ClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/ClassicModelAssertUsageCodeFixTests.cs index 17c47c31..63c73667 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/ClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/ClassicModelAssertUsageCodeFixTests.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis; @@ -22,9 +23,10 @@ private sealed class TestableClassicModelAssertUsageCodeFix : ClassicModelAssert { public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Empty; - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) - { - } + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) => + throw new NotImplementedException(); } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/ContainsClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/ContainsClassicModelAssertUsageCodeFixTests.cs index a491763f..2e137c37 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/ContainsClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/ContainsClassicModelAssertUsageCodeFixTests.cs @@ -27,14 +27,14 @@ public void VerifyGetFixableDiagnosticIds() [Test] public void VerifyContainsFix() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var instance = new object(); var collection = Array.Empty(); ↓ClassicAssert.Contains(instance, collection); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -49,14 +49,14 @@ public void TestMethod() [Test] public void VerifyContainsFixWithMessage() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var instance = new object(); var collection = Array.Empty(); ↓ClassicAssert.Contains(instance, collection, ""message""); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -69,16 +69,16 @@ public void TestMethod() } [Test] - public void VerifyContainsFixWithMessageAndParams() + public void VerifyContainsFixWithMessageAndOneArgumentForParams() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var instance = new object(); var collection = Array.Empty(); - ↓ClassicAssert.Contains(instance, collection, ""message-id: {{0}}"", Guid.NewGuid()); - }}"); + ↓ClassicAssert.Contains(instance, collection, ""message-id: {0}"", Guid.NewGuid()); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -89,5 +89,49 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, instanceDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void VerifyContainsFixWithMessageAndTwoArgumentsForParams() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var instance = new object(); + var collection = Array.Empty(); + + ↓ClassicAssert.Contains(instance, collection, ""{0}, {1}"", ""first"", ""second""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var instance = new object(); + var collection = Array.Empty(); + + Assert.That(collection, Does.Contain(instance), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, instanceDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyContainsFixWithMessageAndArrayParamsInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var instance = new object(); + var collection = Array.Empty(); + + ↓ClassicAssert.Contains(args: new[] { ""first"", ""second"" }, actual: collection, message: ""{0}, {1}"", expected: instance); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var instance = new object(); + var collection = Array.Empty(); + + Assert.That(collection, Does.Contain(instance), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, instanceDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/GreaterClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/GreaterClassicModelAssertUsageCodeFixTests.cs index 75b3568b..13c4cb2d 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/GreaterClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/GreaterClassicModelAssertUsageCodeFixTests.cs @@ -27,11 +27,11 @@ public void VerifyGetFixableDiagnosticIds() [Test] public void VerifyGreaterFix() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { ↓ClassicAssert.Greater(2d, 3d); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -43,11 +43,11 @@ public void TestMethod() [Test] public void VerifyGreaterFixWithMessage() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { ↓ClassicAssert.Greater(2d, 3d, ""message""); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -57,13 +57,13 @@ public void TestMethod() } [Test] - public void VerifyGreaterFixWithMessageAndParams() + public void VerifyGreaterFixWithMessageAndOneArgumentForParams() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ - ↓ClassicAssert.Greater(2d, 3d, ""message-id: {{0}}"", Guid.NewGuid()); - }}"); + { + ↓ClassicAssert.Greater(2d, 3d, ""message-id: {0}"", Guid.NewGuid()); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -72,10 +72,42 @@ public void TestMethod() RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + [Test] + public void VerifyGreaterFixWithMessageAndTwoArgumentsForParams() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + ↓ClassicAssert.Greater(2d, 3d, ""{0}, {1}"", ""first"", ""second""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + Assert.That(2d, Is.GreaterThan(3d), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyGreaterFixWithMessageAndArrayParamsInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + ↓ClassicAssert.Greater(args: new[] { ""first"", ""second"" }, arg2: 3d, message: ""{0}, {1}"", arg1: 2d); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + Assert.That(2d, Is.GreaterThan(3d), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + [Test] public void CodeFixPreservesLineBreakBeforeMessage() { - var code = TestUtility.WrapInTestMethod($@" + var code = TestUtility.WrapInTestMethod(@" ClassicAssert.Greater(2d, 3d, ""message"");"); @@ -161,5 +193,43 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void VerifyGreaterWithImplicitConversionFixInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + private struct MyFloat + { + private readonly float _value; + + public MyFloat(float value) => _value = value; + + public static implicit operator float(MyFloat value) => value._value; + public static implicit operator MyFloat(float value) => new MyFloat(value); + } + public void TestMethod() + { + MyFloat x = 1; + MyFloat y = 2; + ↓ClassicAssert.Greater(args: new[] { ""first"", ""second"" }, message: ""{0}, {1}"", arg2: y, arg1: x); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + private struct MyFloat + { + private readonly float _value; + + public MyFloat(float value) => _value = value; + + public static implicit operator float(MyFloat value) => value._value; + public static implicit operator MyFloat(float value) => new MyFloat(value); + } + public void TestMethod() + { + MyFloat x = 1; + MyFloat y = 2; + Assert.That((float)x, Is.GreaterThan((float)y), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/GreaterOrEqualClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/GreaterOrEqualClassicModelAssertUsageCodeFixTests.cs index 05f77920..b5823959 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/GreaterOrEqualClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/GreaterOrEqualClassicModelAssertUsageCodeFixTests.cs @@ -27,11 +27,11 @@ public void VerifyGetFixableDiagnosticIds() [Test] public void VerifyGreaterOrEqualFix() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { ↓ClassicAssert.GreaterOrEqual(2d, 3d); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -43,11 +43,11 @@ public void TestMethod() [Test] public void VerifyGreaterOrEqualFixWithMessage() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { ↓ClassicAssert.GreaterOrEqual(2d, 3d, ""message""); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -57,13 +57,13 @@ public void TestMethod() } [Test] - public void VerifyGreaterOrEqualFixWithMessageAndParams() + public void VerifyGreaterOrEqualFixWithMessageAndOneArgumentForParams() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ - ↓ClassicAssert.GreaterOrEqual(2d, 3d, ""message-id: {{0}}"", Guid.NewGuid()); - }}"); + { + ↓ClassicAssert.GreaterOrEqual(2d, 3d, ""message-id: {0}"", Guid.NewGuid()); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -72,10 +72,42 @@ public void TestMethod() RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + [Test] + public void VerifyGreaterOrEqualFixWithMessageAndTwoArgumentsForParams() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + ↓ClassicAssert.GreaterOrEqual(2d, 3d, ""{0}, {1}"", ""first"", ""second""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + Assert.That(2d, Is.GreaterThanOrEqualTo(3d), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyGreaterOrEqualFixWithMessageAndArrayParamsInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + ↓ClassicAssert.GreaterOrEqual(args: new[] { ""first"", ""second"" }, arg2: 3d, message: ""{0}, {1}"", arg1: 2d); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + Assert.That(2d, Is.GreaterThanOrEqualTo(3d), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + [Test] public void CodeFixPreservesLineBreakBeforeMessage() { - var code = TestUtility.WrapInTestMethod($@" + var code = TestUtility.WrapInTestMethod(@" ClassicAssert.GreaterOrEqual(2d, 3d, ""message"");"); @@ -161,5 +193,43 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void VerifyGreaterOrEqualWithImplicitConversionFixInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + private struct MyFloat + { + private readonly float _value; + + public MyFloat(float value) => _value = value; + + public static implicit operator float(MyFloat value) => value._value; + public static implicit operator MyFloat(float value) => new MyFloat(value); + } + public void TestMethod() + { + MyFloat x = 1; + MyFloat y = 2; + ↓ClassicAssert.GreaterOrEqual(args: new[] { ""first"", ""second"" }, message: ""{0}, {1}"", arg2: y, arg1: x); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + private struct MyFloat + { + private readonly float _value; + + public MyFloat(float value) => _value = value; + + public static implicit operator float(MyFloat value) => value._value; + public static implicit operator MyFloat(float value) => new MyFloat(value); + } + public void TestMethod() + { + MyFloat x = 1; + MyFloat y = 2; + Assert.That((float)x, Is.GreaterThanOrEqualTo((float)y), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsEmptyClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsEmptyClassicModelAssertUsageCodeFixTests.cs index aa031eb0..756b3b47 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsEmptyClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsEmptyClassicModelAssertUsageCodeFixTests.cs @@ -27,13 +27,13 @@ public void VerifyGetFixableDiagnosticIds() [Test] public void VerifyIsEmptyFix() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var collection = Array.Empty(); ↓ClassicAssert.IsEmpty(collection); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -47,13 +47,13 @@ public void TestMethod() [Test] public void VerifyIsEmptyFixWithMessage() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var collection = Array.Empty(); ↓ClassicAssert.IsEmpty(collection, ""message""); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -65,15 +65,15 @@ public void TestMethod() } [Test] - public void VerifyIsEmptyFixWithMessageAndParams() + public void VerifyIsEmptyFixWithMessageAndOneArgumentForParams() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var collection = Array.Empty(); - ↓ClassicAssert.IsEmpty(collection, ""message-id: {{0}}"", Guid.NewGuid()); - }}"); + ↓ClassicAssert.IsEmpty(collection, ""message-id: {0}"", Guid.NewGuid()); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -84,6 +84,46 @@ public void TestMethod() RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + [Test] + public void VerifyIsEmptyFixWithMessageAndTwoArgumentsForParams() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var collection = Array.Empty(); + + ↓ClassicAssert.IsEmpty(collection, ""{0}, {1}"", ""first"", ""second""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var collection = Array.Empty(); + + Assert.That(collection, Is.Empty, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyIsEmptyFixWithMessageAndArrayParamsInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var collection = Array.Empty(); + + ↓ClassicAssert.IsEmpty(args: new[] { ""first"", ""second"" }, message: ""{0}, {1}"", collection: collection); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var collection = Array.Empty(); + + Assert.That(collection, Is.Empty, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + [Test] public void VerifyIsEmptyWithImplicitTypeConversionFix() { diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsFalseAndFalseClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsFalseAndFalseClassicModelAssertUsageCodeFixTests.cs index 26c1f2f1..65a03a34 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsFalseAndFalseClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsFalseAndFalseClassicModelAssertUsageCodeFixTests.cs @@ -62,7 +62,7 @@ public void TestMethod() [TestCase("IsFalse", AnalyzerIdentifiers.IsFalseUsage)] [TestCase("False", AnalyzerIdentifiers.FalseUsage)] - public void VerifyIsFalseAndFalseFixesWithMessageAndParams(string assertion, string diagnosticId) + public void VerifyIsFalseAndFalseFixesWithMessageAndOneArgumentForParams(string assertion, string diagnosticId) { var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); @@ -79,6 +79,44 @@ public void TestMethod() RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + [TestCase("IsFalse", AnalyzerIdentifiers.IsFalseUsage)] + [TestCase("False", AnalyzerIdentifiers.FalseUsage)] + public void VerifyIsFalseAndFalseFixesWithMessageAndTwoArgumentsForParams(string assertion, string diagnosticId) + { + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.{assertion}(false, ""{{0}}, {{1}}"", ""first"", ""second""); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + Assert.That(false, Is.False, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [TestCase("IsFalse", AnalyzerIdentifiers.IsFalseUsage)] + [TestCase("False", AnalyzerIdentifiers.FalseUsage)] + public void VerifyIsFalseAndFalseFixesWithMessageAndArrayParamsInNonstandardOrder(string assertion, string diagnosticId) + { + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.{assertion}(args: new[] {{ ""first"", ""second"" }}, message: ""{{0}}, {{1}}"", condition: false); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + Assert.That(false, Is.False, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + [TestCase("IsFalse", AnalyzerIdentifiers.IsFalseUsage)] [TestCase("False", AnalyzerIdentifiers.FalseUsage)] public void VerifyIsFalseAndFalseWithImplicitTypeConversionFixes(string assertion, string diagnosticId) diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsInstanceOfClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsInstanceOfClassicModelAssertUsageCodeFixTests.cs index cc8fa63d..9c26ada2 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsInstanceOfClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsInstanceOfClassicModelAssertUsageCodeFixTests.cs @@ -27,14 +27,14 @@ public void VerifyGetFixableDiagnosticIds() [Test] public void VerifyIsInstanceOfFix() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expected = typeof(int); var actual = 42; ↓ClassicAssert.IsInstanceOf(expected, actual); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -49,14 +49,14 @@ public void TestMethod() [Test] public void VerifyIsInstanceOfFixWithMessage() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expected = typeof(int); var actual = 42; ↓ClassicAssert.IsInstanceOf(expected, actual, ""message""); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -69,16 +69,16 @@ public void TestMethod() } [Test] - public void VerifyIsInstanceOfFixWithMessageAndParams() + public void VerifyIsInstanceOfFixWithMessageAndOneArgumentForParams() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expected = typeof(int); var actual = 42; - ↓ClassicAssert.IsInstanceOf(expected, actual, ""message-id: {{0}}"", Guid.NewGuid()); - }}"); + ↓ClassicAssert.IsInstanceOf(expected, actual, ""message-id: {0}"", Guid.NewGuid()); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -90,16 +90,60 @@ public void TestMethod() RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + [Test] + public void VerifyIsInstanceOfFixWithMessageAndTwoArgumentsForParams() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = typeof(int); + var actual = 42; + + ↓ClassicAssert.IsInstanceOf(expected, actual, ""{0}, {1}"", ""first"", ""second""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = typeof(int); + var actual = 42; + + Assert.That(actual, Is.InstanceOf(expected), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyIsInstanceOfFixWithMessageAndArrayParamsInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = typeof(int); + var actual = 42; + + ↓ClassicAssert.IsInstanceOf(args: new[] { ""first"", ""second"" }, message: ""{0}, {1}"", actual: actual, expected: expected); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = typeof(int); + var actual = 42; + + Assert.That(actual, Is.InstanceOf(expected), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + [Test] public void VerifyIsInstanceOfGenericFix() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var actual = 42; ↓ClassicAssert.IsInstanceOf(actual); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -199,13 +243,13 @@ public Wrapped(T value) [Test] public void VerifyIsInstanceOfGenericFixWithMessage() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var actual = 42; ↓ClassicAssert.IsInstanceOf(actual, ""message""); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -217,17 +261,45 @@ public void TestMethod() } [Test] - public void VerifyIsInstanceOfGenericFixWithMessageAndParams() + public void VerifyIsInstanceOfGenericFixWithMessageAndOneArgumentForParams() { - var code = TestUtility.WrapInTestMethod($@" + var code = TestUtility.WrapInTestMethod(@" var actual = 42; - ↓ClassicAssert.IsInstanceOf(actual, ""message-id: {{0}}"", Guid.NewGuid());"); + ↓ClassicAssert.IsInstanceOf(actual, ""message-id: {0}"", Guid.NewGuid());"); var fixedCode = TestUtility.WrapInTestMethod(@" var actual = 42; Assert.That(actual, Is.InstanceOf(), $""message-id: {Guid.NewGuid()}"");"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void VerifyIsInstanceOfGenericFixWithMessageAndTwoArgumentsForParams() + { + var code = TestUtility.WrapInTestMethod(@" + var actual = 42; + + ↓ClassicAssert.IsInstanceOf(actual, ""{0}, {1}"", ""first"", ""second"");"); + var fixedCode = TestUtility.WrapInTestMethod(@" + var actual = 42; + + Assert.That(actual, Is.InstanceOf(), $""{""first""}, {""second""}"");"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyIsInstanceOfGenericFixWithMessageAndArrayParamsInNonstandardOrder() + { + var code = TestUtility.WrapInTestMethod(@" + var actual = 42; + + ↓ClassicAssert.IsInstanceOf(args: new[] { ""first"", ""second"" }, message: ""{0}, {1}"", actual: actual);"); + var fixedCode = TestUtility.WrapInTestMethod(@" + var actual = 42; + + Assert.That(actual, Is.InstanceOf(), $""{""first""}, {""second""}"");"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNaNClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNaNClassicModelAssertUsageCodeFixTests.cs index cc5a8649..d474e249 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNaNClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNaNClassicModelAssertUsageCodeFixTests.cs @@ -27,13 +27,13 @@ public void VerifyGetFixableDiagnosticIds() [Test] public void VerifyIsNaNFix() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expr = double.NaN; ↓ClassicAssert.IsNaN(expr); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -47,13 +47,13 @@ public void TestMethod() [Test] public void VerifyIsNaNFixWithMessage() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expr = double.NaN; ↓ClassicAssert.IsNaN(expr, ""message""); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -65,15 +65,15 @@ public void TestMethod() } [Test] - public void VerifyIsNaNFixWithMessageAndParams() + public void VerifyIsNaNFixWithMessageAndOneArgumentForParams() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expr = double.NaN; - ↓ClassicAssert.IsNaN(expr, ""message-id: {{0}}"", Guid.NewGuid()); - }}"); + ↓ClassicAssert.IsNaN(expr, ""message-id: {0}"", Guid.NewGuid()); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -83,5 +83,45 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void VerifyIsNaNFixWithMessageAndTwoArgumentsForParams() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expr = double.NaN; + + ↓ClassicAssert.IsNaN(expr, ""{0}, {1}"", ""first"", ""second""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expr = double.NaN; + + Assert.That(expr, Is.NaN, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyIsNaNFixWithMessageAndArrayParamsInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expr = double.NaN; + + ↓ClassicAssert.IsNaN(args: new[] { ""first"", ""second"" }, aDouble: expr, message: ""{0}, {1}""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expr = double.NaN; + + Assert.That(expr, Is.NaN, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotEmptyClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotEmptyClassicModelAssertUsageCodeFixTests.cs index f24d963f..b410f513 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotEmptyClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotEmptyClassicModelAssertUsageCodeFixTests.cs @@ -27,13 +27,13 @@ public void VerifyGetFixableDiagnosticIds() [Test] public void VerifyIsNotEmptyFix() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var collection = Array.Empty(); ↓ClassicAssert.IsNotEmpty(collection); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -47,13 +47,13 @@ public void TestMethod() [Test] public void VerifyIsNotEmptyFixWithMessage() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var collection = Array.Empty(); ↓ClassicAssert.IsNotEmpty(collection, ""message""); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -65,15 +65,15 @@ public void TestMethod() } [Test] - public void VerifyIsNotEmptyFixWithMessageAndParams() + public void VerifyIsNotEmptyFixWithMessageAndOneArgumentForParams() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var collection = Array.Empty(); - ↓ClassicAssert.IsNotEmpty(collection, ""message-id: {{0}}"", Guid.NewGuid()); - }}"); + ↓ClassicAssert.IsNotEmpty(collection, ""message-id: {0}"", Guid.NewGuid()); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -84,6 +84,46 @@ public void TestMethod() RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + [Test] + public void VerifyIsNotEmptyFixWithMessageAndTwoArgumentsForParams() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var collection = Array.Empty(); + + ↓ClassicAssert.IsNotEmpty(collection, ""{0}, {1}"", ""first"", ""second""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var collection = Array.Empty(); + + Assert.That(collection, Is.Not.Empty, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyIsNotEmptyFixWithMessageAndArrayParamsInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var collection = Array.Empty(); + + ↓ClassicAssert.IsNotEmpty(args: new[] { ""first"", ""second"" }, collection: collection, message: ""{0}, {1}""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var collection = Array.Empty(); + + Assert.That(collection, Is.Not.Empty, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + [Test] public void VerifyIsNotEmptyWithImplicitTypeConversionFix() { @@ -119,5 +159,41 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void VerifyIsNotEmptyWithImplicitTypeConversionFixInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + private struct MyString + { + private readonly string _value; + + public MyString(string value) => _value = value; + + public static implicit operator string(MyString value) => value._value; + public static implicit operator MyString(string value) => new MyString(value); + } + public void TestMethod() + { + MyString s = ""Hello NUnit""; + ↓ClassicAssert.IsNotEmpty(args: new[] { ""first"", ""second"" }, aString: s, message: ""{0}, {1}""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + private struct MyString + { + private readonly string _value; + + public MyString(string value) => _value = value; + + public static implicit operator string(MyString value) => value._value; + public static implicit operator MyString(string value) => new MyString(value); + } + public void TestMethod() + { + MyString s = ""Hello NUnit""; + Assert.That((string)s, Is.Not.Empty, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotInstanceOfClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotInstanceOfClassicModelAssertUsageCodeFixTests.cs index 2529f035..259cd1bd 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotInstanceOfClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotInstanceOfClassicModelAssertUsageCodeFixTests.cs @@ -27,14 +27,14 @@ public void VerifyGetFixableDiagnosticIds() [Test] public void VerifyIsNotInstanceOfFix() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expected = typeof(int); var actual = 42; ↓ClassicAssert.IsNotInstanceOf(expected, actual); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -49,14 +49,14 @@ public void TestMethod() [Test] public void VerifyIsNotInstanceOfFixWithMessage() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expected = typeof(int); var actual = 42; ↓ClassicAssert.IsNotInstanceOf(expected, actual, ""message""); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -69,16 +69,16 @@ public void TestMethod() } [Test] - public void VerifyIsNotInstanceOfFixWithMessageAndParams() + public void VerifyIsNotInstanceOfFixWithMessageAndOneArgumentForParams() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expected = typeof(int); var actual = 42; - ↓ClassicAssert.IsNotInstanceOf(expected, actual, ""message-id: {{0}}"", Guid.NewGuid()); - }}"); + ↓ClassicAssert.IsNotInstanceOf(expected, actual, ""message-id: {0}"", Guid.NewGuid()); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -90,16 +90,60 @@ public void TestMethod() RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + [Test] + public void VerifyIsNotInstanceOfFixWithMessageAndTwoArgumentsForParams() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = typeof(int); + var actual = 42; + + ↓ClassicAssert.IsNotInstanceOf(expected, actual, ""{0}, {1}"", ""first"", ""second""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = typeof(int); + var actual = 42; + + Assert.That(actual, Is.Not.InstanceOf(expected), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyIsNotInstanceOfFixWithMessageAndArrayParamsInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = typeof(int); + var actual = 42; + + ↓ClassicAssert.IsNotInstanceOf(args: new[] { ""first"", ""second"" }, message: ""{0}, {1}"", actual: actual, expected: expected); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = typeof(int); + var actual = 42; + + Assert.That(actual, Is.Not.InstanceOf(expected), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + [Test] public void VerifyIsNotInstanceOfGenericFix() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var actual = 42; ↓ClassicAssert.IsNotInstanceOf(actual); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -199,13 +243,13 @@ public Wrapped(T value) [Test] public void VerifyIsNotInstanceOfGenericFixWithMessage() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var actual = 42; ↓ClassicAssert.IsNotInstanceOf(actual, ""message""); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -217,17 +261,45 @@ public void TestMethod() } [Test] - public void VerifyIsNotInstanceOfGenericFixWithMessageAndParams() + public void VerifyIsNotInstanceOfGenericFixWithMessageAndOneArgumentForParams() { - var code = TestUtility.WrapInTestMethod($@" + var code = TestUtility.WrapInTestMethod(@" var actual = 42; - ↓ClassicAssert.IsNotInstanceOf(actual, ""message-id: {{0}}"", Guid.NewGuid());"); + ↓ClassicAssert.IsNotInstanceOf(actual, ""message-id: {0}"", Guid.NewGuid());"); var fixedCode = TestUtility.WrapInTestMethod(@" var actual = 42; Assert.That(actual, Is.Not.InstanceOf(), $""message-id: {Guid.NewGuid()}"");"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void VerifyIsNotInstanceOfGenericFixWithMessageAndTwoArgumentsForParams() + { + var code = TestUtility.WrapInTestMethod(@" + var actual = 42; + + ↓ClassicAssert.IsNotInstanceOf(actual, ""{0}, {1}"", ""first"", ""second"");"); + var fixedCode = TestUtility.WrapInTestMethod(@" + var actual = 42; + + Assert.That(actual, Is.Not.InstanceOf(), $""{""first""}, {""second""}"");"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyIsNotInstanceOfGenericFixWithMessageAndArrayParamsInNonstandardOrder() + { + var code = TestUtility.WrapInTestMethod(@" + var actual = 42; + + ↓ClassicAssert.IsNotInstanceOf(args: new[] { ""first"", ""second"" }, message: ""{0}, {1}"", actual: actual);"); + var fixedCode = TestUtility.WrapInTestMethod(@" + var actual = 42; + + Assert.That(actual, Is.Not.InstanceOf(), $""{""first""}, {""second""}"");"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotNullAndNotNullClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotNullAndNotNullClassicModelAssertUsageCodeFixTests.cs index b270b290..f1c37fdc 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotNullAndNotNullClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotNullAndNotNullClassicModelAssertUsageCodeFixTests.cs @@ -66,7 +66,7 @@ public void TestMethod() [TestCase("IsNotNull", AnalyzerIdentifiers.IsNotNullUsage)] [TestCase("NotNull", AnalyzerIdentifiers.NotNullUsage)] - public void VerifyIsNotNullAndNotNullFixesWithMessageAndParams(string assertion, string diagnosticId) + public void VerifyIsNotNullAndNotNullFixesWithMessageAndOneArgumentForParams(string assertion, string diagnosticId) { var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); @@ -84,5 +84,47 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [TestCase("IsNotNull", AnalyzerIdentifiers.IsNotNullUsage)] + [TestCase("NotNull", AnalyzerIdentifiers.NotNullUsage)] + public void VerifyIsNotNullAndNotNullFixesWithMessageAndTwoArgumentsForParams(string assertion, string diagnosticId) + { + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + object? obj = null; + ↓ClassicAssert.{assertion}(obj, ""{{0}}, {{1}}"", ""first"", ""second""); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + object? obj = null; + Assert.That(obj, Is.Not.Null, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [TestCase("IsNotNull", AnalyzerIdentifiers.IsNotNullUsage)] + [TestCase("NotNull", AnalyzerIdentifiers.NotNullUsage)] + public void VerifyIsNotNullAndNotNullFixesWithMessageAndArrayParamsInNonstandardOrder(string assertion, string diagnosticId) + { + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + object? obj = null; + ↓ClassicAssert.{assertion}(args: new[] {{ ""first"", ""second"" }}, message: ""{{0}}, {{1}}"", anObject: obj); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + object? obj = null; + Assert.That(obj, Is.Not.Null, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNullAndNullClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNullAndNullClassicModelAssertUsageCodeFixTests.cs index 638b821e..6c517696 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNullAndNullClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNullAndNullClassicModelAssertUsageCodeFixTests.cs @@ -66,7 +66,7 @@ public void TestMethod() [TestCase("IsNull", AnalyzerIdentifiers.IsNullUsage)] [TestCase("Null", AnalyzerIdentifiers.NullUsage)] - public void VerifyIsNullAndNullFixesWithMessageAndParams(string assertion, string diagnosticId) + public void VerifyIsNullAndNullFixesWithMessageAndOneArgumentForParams(string assertion, string diagnosticId) { var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); @@ -84,5 +84,47 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [TestCase("IsNull", AnalyzerIdentifiers.IsNullUsage)] + [TestCase("Null", AnalyzerIdentifiers.NullUsage)] + public void VerifyIsNullAndNullFixesWithMessageAndTwoArgumentsForParams(string assertion, string diagnosticId) + { + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + object? obj = null; + ↓ClassicAssert.{assertion}(obj, ""{{0}}, {{1}}"", ""first"", ""second""); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + object? obj = null; + Assert.That(obj, Is.Null, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [TestCase("IsNull", AnalyzerIdentifiers.IsNullUsage)] + [TestCase("Null", AnalyzerIdentifiers.NullUsage)] + public void VerifyIsNullAndNullFixesWithMessageAndArrayParamsInNonstandardOrder(string assertion, string diagnosticId) + { + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + object? obj = null; + ↓ClassicAssert.{assertion}(args: new[] {{ ""first"", ""second"" }}, message: ""{{0}}, {{1}}"", anObject: obj); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + object? obj = null; + Assert.That(obj, Is.Null, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCodeFixTests.cs index 9f093c0b..be20d208 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCodeFixTests.cs @@ -62,7 +62,7 @@ public void TestMethod() [TestCase("IsTrue", AnalyzerIdentifiers.IsTrueUsage)] [TestCase("True", AnalyzerIdentifiers.TrueUsage)] - public void VerifyIsTrueAndTrueFixesWithMessageAndParams(string assertion, string diagnosticId) + public void VerifyIsTrueAndTrueFixesWithMessageAndOneArgumentForParams(string assertion, string diagnosticId) { var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); @@ -79,6 +79,44 @@ public void TestMethod() RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + [TestCase("IsTrue", AnalyzerIdentifiers.IsTrueUsage)] + [TestCase("True", AnalyzerIdentifiers.TrueUsage)] + public void VerifyIsTrueAndTrueFixesWithMessageAndTwoArgumentsForParams(string assertion, string diagnosticId) + { + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.{assertion}(true, ""{{0}}, {{1}}"", ""first"", ""second""); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + Assert.That(true, Is.True, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [TestCase("IsTrue", AnalyzerIdentifiers.IsTrueUsage)] + [TestCase("True", AnalyzerIdentifiers.TrueUsage)] + public void VerifyIsTrueAndTrueFixesWithMessageAndArrayParamsInNonstandardOrder(string assertion, string diagnosticId) + { + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.{assertion}(args: new[] {{ ""first"", ""second"" }}, message: ""{{0}}, {{1}}"", condition: true); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + Assert.That(true, Is.True, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + [TestCase("IsTrue", AnalyzerIdentifiers.IsTrueUsage)] [TestCase("True", AnalyzerIdentifiers.TrueUsage)] public void VerifyIsTrueAndTrueWithImplicitTypeConversionFixes(string assertion, string diagnosticId) @@ -117,5 +155,44 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [TestCase("IsTrue", AnalyzerIdentifiers.IsTrueUsage)] + [TestCase("True", AnalyzerIdentifiers.TrueUsage)] + public void VerifyIsTrueAndTrueWithImplicitTypeConversionFixesInNonstandardOrder(string assertion, string diagnosticId) + { + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + private struct MyBool + {{ + private readonly bool _value; + + public MyBool(bool value) => _value = value; + + public static implicit operator bool(MyBool value) => value._value; + public static implicit operator MyBool(bool value) => new MyBool(value); + }} + public void TestMethod() + {{ + MyBool x = true; + ↓ClassicAssert.{assertion}(args: new[] {{ ""first"", ""second"" }}, message: ""{{0}}, {{1}}"", condition: x); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + private struct MyBool + { + private readonly bool _value; + + public MyBool(bool value) => _value = value; + + public static implicit operator bool(MyBool value) => value._value; + public static implicit operator MyBool(bool value) => new MyBool(value); + } + public void TestMethod() + { + MyBool x = true; + Assert.That((bool)x, Is.True, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCondensedCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCondensedCodeFixTests.cs index 2505d814..2f840b1d 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCondensedCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCondensedCodeFixTests.cs @@ -64,7 +64,7 @@ public void TestMethod() [TestCase("IsTrue", AnalyzerIdentifiers.IsTrueUsage)] [TestCase("True", AnalyzerIdentifiers.TrueUsage)] - public void VerifyIsTrueAndTrueFixesWithMessageAndParams(string assertion, string diagnosticId) + public void VerifyIsTrueAndTrueFixesWithMessageAndOneArgumentForParams(string assertion, string diagnosticId) { var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); @@ -81,5 +81,45 @@ public void TestMethod() RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription + IsTrueAndTrueClassicModelAssertUsageCondensedCodeFix.Suffix); } + + [TestCase("IsTrue", AnalyzerIdentifiers.IsTrueUsage)] + [TestCase("True", AnalyzerIdentifiers.TrueUsage)] + public void VerifyIsTrueAndTrueFixesWithMessageAndTwoArgumentsForParams(string assertion, string diagnosticId) + { + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.{assertion}(true, ""{{0}}, {{1}}"", ""first"", ""second""); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + Assert.That(true, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, + fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription + IsTrueAndTrueClassicModelAssertUsageCondensedCodeFix.Suffix); + } + + [TestCase("IsTrue", AnalyzerIdentifiers.IsTrueUsage)] + [TestCase("True", AnalyzerIdentifiers.TrueUsage)] + public void VerifyIsTrueAndTrueFixesWithMessageAndArrayParamsInNonstandardOrder(string assertion, string diagnosticId) + { + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.{assertion}(args: new[] {{ ""first"", ""second"" }}, message: ""{{0}}, {{1}}"", condition: true); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + Assert.That(true, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, + fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription + IsTrueAndTrueClassicModelAssertUsageCondensedCodeFix.Suffix); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/LessClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/LessClassicModelAssertUsageCodeFixTests.cs index 76a802f5..f1198094 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/LessClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/LessClassicModelAssertUsageCodeFixTests.cs @@ -27,11 +27,11 @@ public void VerifyGetFixableDiagnosticIds() [Test] public void VerifyLessFix() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { ↓ClassicAssert.Less(2d, 3d); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -43,11 +43,11 @@ public void TestMethod() [Test] public void VerifyLessFixWithMessage() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { ↓ClassicAssert.Less(2d, 3d, ""message""); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -57,13 +57,13 @@ public void TestMethod() } [Test] - public void VerifyLessFixWithMessageAndParams() + public void VerifyLessFixWithMessageAndOneArgumentForParams() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ - ↓ClassicAssert.Less(2d, 3d, ""message-id: {{0}}"", Guid.NewGuid()); - }}"); + { + ↓ClassicAssert.Less(2d, 3d, ""message-id: {0}"", Guid.NewGuid()); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -72,10 +72,42 @@ public void TestMethod() RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + [Test] + public void VerifyLessFixWithMessageAndTwoArgumentsForParams() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + ↓ClassicAssert.Less(2d, 3d, ""{0}, {1}"", ""first"", ""second""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + Assert.That(2d, Is.LessThan(3d), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyLessFixWithMessageAndArrayParamsInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + ↓ClassicAssert.Less(args: new[] { ""first"", ""second"" }, arg2: 3d, message: ""{0}, {1}"", arg1: 2d); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + Assert.That(2d, Is.LessThan(3d), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + [Test] public void CodeFixPreservesLineBreakBeforeMessage() { - var code = TestUtility.WrapInTestMethod($@" + var code = TestUtility.WrapInTestMethod(@" ClassicAssert.Less(2d, 3d, ""message"");"); @@ -161,5 +193,43 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void VerifyLessWithImplicitConversionFixInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + private struct MyFloat + { + private readonly float _value; + + public MyFloat(float value) => _value = value; + + public static implicit operator float(MyFloat value) => value._value; + public static implicit operator MyFloat(float value) => new MyFloat(value); + } + public void TestMethod() + { + MyFloat x = 1; + MyFloat y = 2; + ↓ClassicAssert.Less(args: new[] { ""first"", ""second"" }, message: ""{0}, {1}"", arg2: y, arg1: x); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + private struct MyFloat + { + private readonly float _value; + + public MyFloat(float value) => _value = value; + + public static implicit operator float(MyFloat value) => value._value; + public static implicit operator MyFloat(float value) => new MyFloat(value); + } + public void TestMethod() + { + MyFloat x = 1; + MyFloat y = 2; + Assert.That((float)x, Is.LessThan((float)y), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/LessOrEqualClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/LessOrEqualClassicModelAssertUsageCodeFixTests.cs index 22654092..c54be504 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/LessOrEqualClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/LessOrEqualClassicModelAssertUsageCodeFixTests.cs @@ -27,11 +27,11 @@ public void VerifyGetFixableDiagnosticIds() [Test] public void VerifyLessOrEqualFix() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { ↓ClassicAssert.LessOrEqual(2d, 3d); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -43,11 +43,11 @@ public void TestMethod() [Test] public void VerifyLessOrEqualFixWithMessage() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { ↓ClassicAssert.LessOrEqual(2d, 3d, ""message""); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -57,13 +57,13 @@ public void TestMethod() } [Test] - public void VerifyLessOrEqualFixWithMessageAndParams() + public void VerifyLessOrEqualFixWithMessageAndOneArgumentForParams() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ - ↓ClassicAssert.LessOrEqual(2d, 3d, ""message-id: {{0}}"", Guid.NewGuid()); - }}"); + { + ↓ClassicAssert.LessOrEqual(2d, 3d, ""message-id: {0}"", Guid.NewGuid()); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -72,10 +72,42 @@ public void TestMethod() RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + [Test] + public void VerifyLessOrEqualFixWithMessageAndTwoArgumentsForParams() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + ↓ClassicAssert.LessOrEqual(2d, 3d, ""{0}, {1}"", ""first"", ""second""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + Assert.That(2d, Is.LessThanOrEqualTo(3d), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyLessOrEqualFixWithMessageAndArrayParamsInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + ↓ClassicAssert.LessOrEqual(args: new[] { ""first"", ""second"" }, arg2: 3d, message: ""{0}, {1}"", arg1: 2d); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + Assert.That(2d, Is.LessThanOrEqualTo(3d), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + [Test] public void CodeFixPreservesLineBreakBeforeMessage() { - var code = TestUtility.WrapInTestMethod($@" + var code = TestUtility.WrapInTestMethod(@" ClassicAssert.LessOrEqual(2d, 3d, ""message"");"); @@ -161,5 +193,43 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void VerifyLessOrEqualWithImplicitConversionFixInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + private struct MyFloat + { + private readonly float _value; + + public MyFloat(float value) => _value = value; + + public static implicit operator float(MyFloat value) => value._value; + public static implicit operator MyFloat(float value) => new MyFloat(value); + } + public void TestMethod() + { + MyFloat x = 1; + MyFloat y = 2; + ↓ClassicAssert.LessOrEqual(args: new[] { ""first"", ""second"" }, message: ""{0}, {1}"", arg2: y, arg1: x); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + private struct MyFloat + { + private readonly float _value; + + public MyFloat(float value) => _value = value; + + public static implicit operator float(MyFloat value) => value._value; + public static implicit operator MyFloat(float value) => new MyFloat(value); + } + public void TestMethod() + { + MyFloat x = 1; + MyFloat y = 2; + Assert.That((float)x, Is.LessThanOrEqualTo((float)y), $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/NotZeroClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/NotZeroClassicModelAssertUsageCodeFixTests.cs index db0cca19..d05fbe00 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/NotZeroClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/NotZeroClassicModelAssertUsageCodeFixTests.cs @@ -27,13 +27,13 @@ public void VerifyGetFixableDiagnosticIds() [Test] public void VerifyNotZeroFix() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expr = default(int); ↓ClassicAssert.NotZero(expr); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -47,13 +47,13 @@ public void TestMethod() [Test] public void VerifyNotZeroFixWithMessage() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expr = default(int); ↓ClassicAssert.NotZero(expr, ""message""); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -65,15 +65,15 @@ public void TestMethod() } [Test] - public void VerifyNotZeroFixWithMessageAndParams() + public void VerifyNotZeroFixWithMessageAndOneArgumentForParams() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expr = default(int); - ↓ClassicAssert.NotZero(expr, ""message-id: {{0}}"", Guid.NewGuid()); - }}"); + ↓ClassicAssert.NotZero(expr, ""message-id: {0}"", Guid.NewGuid()); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -83,5 +83,45 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void VerifyNotZeroFixWithMessageAndTwoArgumentsForParams() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expr = default(int); + + ↓ClassicAssert.NotZero(expr, ""{0}, {1}"", ""first"", ""second""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expr = default(int); + + Assert.That(expr, Is.Not.Zero, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyNotZeroFixWithMessageAndArrayParamsInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expr = default(int); + + ↓ClassicAssert.NotZero(args: new[] { ""first"", ""second"" }, message: ""{0}, {1}"", actual: expr); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expr = default(int); + + Assert.That(expr, Is.Not.Zero, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/ZeroClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/ZeroClassicModelAssertUsageCodeFixTests.cs index 6f326729..5b8fdbe2 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/ZeroClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/ZeroClassicModelAssertUsageCodeFixTests.cs @@ -27,13 +27,13 @@ public void VerifyGetFixableDiagnosticIds() [Test] public void VerifyZeroFix() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expr = default(int); ↓ClassicAssert.Zero(expr); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -47,13 +47,13 @@ public void TestMethod() [Test] public void VerifyZeroFixWithMessage() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expr = default(int); ↓ClassicAssert.Zero(expr, ""message""); - }}"); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -65,15 +65,15 @@ public void TestMethod() } [Test] - public void VerifyZeroFixWithMessageAndParams() + public void VerifyZeroFixWithMessageAndOneArgumentForParams() { - var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() - {{ + { var expr = default(int); - ↓ClassicAssert.Zero(expr, ""message-id: {{0}}"", Guid.NewGuid()); - }}"); + ↓ClassicAssert.Zero(expr, ""message-id: {0}"", Guid.NewGuid()); + }"); var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" public void TestMethod() { @@ -83,5 +83,45 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void VerifyZeroFixWithMessageAndTwoArgumentsForParams() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expr = default(int); + + ↓ClassicAssert.Zero(expr, ""{0}, {1}"", ""first"", ""second""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expr = default(int); + + Assert.That(expr, Is.Zero, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void VerifyZeroFixWithMessageAndArrayParamsInNonstandardOrder() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expr = default(int); + + ↓ClassicAssert.Zero(args: new[] { ""first"", ""second"" }, message: ""{0}, {1}"", actual: expr); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expr = default(int); + + Assert.That(expr, Is.Zero, $""{""first""}, {""second""}""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/CollectionAssertUsage/CollectionAssertUsageAnalyzerTests.cs b/src/nunit.analyzers.tests/CollectionAssertUsage/CollectionAssertUsageAnalyzerTests.cs index b2ae923d..96d13729 100644 --- a/src/nunit.analyzers.tests/CollectionAssertUsage/CollectionAssertUsageAnalyzerTests.cs +++ b/src/nunit.analyzers.tests/CollectionAssertUsage/CollectionAssertUsageAnalyzerTests.cs @@ -30,7 +30,7 @@ public void AnalyzeOneCollectionWhenNoMessageArgumentsAreUsed(string method) } [TestCaseSource(nameof(OneCollectionParameterAsserts))] - public void AnalyzeOneCollectionWhenOnlyMessageArgumentsAreUsed(string method) + public void AnalyzeOneCollectionWhenOnlyMessageArgumentIsUsed(string method) { var testCode = TestUtility.WrapInTestMethod(@$" var collection = new[] {{ 1, 2, 3 }}; @@ -40,7 +40,7 @@ public void AnalyzeOneCollectionWhenOnlyMessageArgumentsAreUsed(string method) } [TestCaseSource(nameof(OneCollectionParameterAsserts))] - public void AnalyzeOneCollectionWhenFormatAndParamsArgumentsAreUsed(string method) + public void AnalyzeOneCollectionWhenFormatAndOneParamsArgumentAreUsed(string method) { var testCode = TestUtility.WrapInTestMethod(@$" var collection = new[] {{ 1, 2, 3 }}; @@ -49,6 +49,16 @@ public void AnalyzeOneCollectionWhenFormatAndParamsArgumentsAreUsed(string metho RoslynAssert.Diagnostics(this.analyzer, this.diagnostic, testCode); } + [TestCaseSource(nameof(OneCollectionParameterAsserts))] + public void AnalyzeOneCollectionWhenFormatAndTwoParamsArgumentsAreUsed(string method) + { + var testCode = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ 1, 2, 3 }}; + ↓CollectionAssert.{method}(collection, ""{{0}}, {{1}}"", ""first"", ""second""); + "); + RoslynAssert.Diagnostics(this.analyzer, this.diagnostic, testCode); + } + [TestCaseSource(nameof(TwoCollectionParameterAsserts))] public void AnalyzeTwoCollectionWhenNoMessageArgumentsAreUsed(string method) { @@ -61,7 +71,7 @@ public void AnalyzeTwoCollectionWhenNoMessageArgumentsAreUsed(string method) } [TestCaseSource(nameof(TwoCollectionParameterAsserts))] - public void AnalyzeTwoCollectionWhenOnlyMessageArgumentsAreUsed(string method) + public void AnalyzeTwoCollectionWhenOnlyMessageArgumentIsUsed(string method) { var testCode = TestUtility.WrapInTestMethod(@$" var collection1 = new[] {{ 1, 2, 3 }}; @@ -72,7 +82,7 @@ public void AnalyzeTwoCollectionWhenOnlyMessageArgumentsAreUsed(string method) } [TestCaseSource(nameof(TwoCollectionParameterAsserts))] - public void AnalyzeTwoCollectionWhenFormatAndParamsArgumentsAreUsed(string method) + public void AnalyzeTwoCollectionWhenFormatAndOneParamsArgumentAreUsed(string method) { var testCode = TestUtility.WrapInTestMethod(@$" var collection1 = new[] {{ 1, 2, 3 }}; @@ -82,6 +92,17 @@ public void AnalyzeTwoCollectionWhenFormatAndParamsArgumentsAreUsed(string metho RoslynAssert.Diagnostics(this.analyzer, this.diagnostic, testCode); } + [TestCaseSource(nameof(TwoCollectionParameterAsserts))] + public void AnalyzeTwoCollectionWhenFormatAndTwoParamsArgumentsAreUsed(string method) + { + var testCode = TestUtility.WrapInTestMethod(@$" + var collection1 = new[] {{ 1, 2, 3 }}; + var collection2 = new[] {{ 2, 4, 6 }}; + ↓CollectionAssert.{method}(collection1, collection2, ""{{0}}, {{1}}"", ""first"", ""second""); + "); + RoslynAssert.Diagnostics(this.analyzer, this.diagnostic, testCode); + } + [TestCase(NUnitFrameworkConstants.NameOfCollectionAssertAreEqual)] [TestCase(NUnitFrameworkConstants.NameOfCollectionAssertAreNotEqual)] public void AnalyzeTwoCollectionWithComparerWhenFormatAndParamsArgumentsAreUsed(string method) @@ -115,7 +136,7 @@ public void AnalyzeCollectionAndItemWhenNoMessageArgumentsAreUsed(string method) } [TestCaseSource(nameof(CollectionAndItemParameterAsserts))] - public void AnalyzeCollectionAndItemWhenOnlyMessageArgumentsAreUsed(string method) + public void AnalyzeCollectionAndItemWhenOnlyMessageArgumentIsUsed(string method) { var testCode = TestUtility.WrapInTestMethod(@$" var collection = new[] {{ typeof(byte), typeof(char) }}; @@ -125,7 +146,7 @@ public void AnalyzeCollectionAndItemWhenOnlyMessageArgumentsAreUsed(string metho } [TestCaseSource(nameof(CollectionAndItemParameterAsserts))] - public void AnalyzeCollectionAndItemWhenFormatAndParamsArgumentsAreUsed(string method) + public void AnalyzeCollectionAndItemWhenFormatAndOneParamsArgumentAreUsed(string method) { var testCode = TestUtility.WrapInTestMethod(@$" var collection = new[] {{ typeof(byte), typeof(char) }}; @@ -133,5 +154,15 @@ public void AnalyzeCollectionAndItemWhenFormatAndParamsArgumentsAreUsed(string m "); RoslynAssert.Diagnostics(this.analyzer, this.diagnostic, testCode); } + + [TestCaseSource(nameof(CollectionAndItemParameterAsserts))] + public void AnalyzeCollectionAndItemWhenFormatAndTwoParamsArgumentsAreUsed(string method) + { + var testCode = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ typeof(byte), typeof(char) }}; + ↓CollectionAssert.{method}(collection, typeof(byte), ""{{0}}, {{1}}"", ""first"", ""second""); + "); + RoslynAssert.Diagnostics(this.analyzer, this.diagnostic, testCode); + } } } diff --git a/src/nunit.analyzers.tests/CollectionAssertUsage/CollectionAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/CollectionAssertUsage/CollectionAssertUsageCodeFixTests.cs index b0cd749a..847992bd 100644 --- a/src/nunit.analyzers.tests/CollectionAssertUsage/CollectionAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/CollectionAssertUsage/CollectionAssertUsageCodeFixTests.cs @@ -36,7 +36,7 @@ public void AnalyzeOneCollectionWhenNoMessageArgumentsAreUsed(string method) } [TestCaseSource(nameof(OneCollectionParameterAsserts))] - public void AnalyzeOneCollectionWhenOnlyMessageArgumentsAreUsed(string method) + public void AnalyzeOneCollectionWhenOnlyMessageArgumentIsUsed(string method) { var code = TestUtility.WrapInTestMethod(@$" var collection = new[] {{ 1, 2, 3 }}; @@ -50,7 +50,7 @@ public void AnalyzeOneCollectionWhenOnlyMessageArgumentsAreUsed(string method) } [TestCaseSource(nameof(OneCollectionParameterAsserts))] - public void AnalyzeOneCollectionWhenFormatAndParamsArgumentsAreUsed(string method) + public void AnalyzeOneCollectionWhenFormatAndOneParamsArgumentAreUsed(string method) { var code = TestUtility.WrapInTestMethod(@$" var collection = new[] {{ 1, 2, 3 }}; @@ -63,6 +63,34 @@ public void AnalyzeOneCollectionWhenFormatAndParamsArgumentsAreUsed(string metho RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); } + [TestCaseSource(nameof(OneCollectionParameterAsserts))] + public void AnalyzeOneCollectionWhenFormatAndTwoParamsArgumentsAreUsed(string method) + { + var code = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ 1, 2, 3 }}; + ↓CollectionAssert.{method}(collection, ""{{0}}, {{1}}"", ""first"", ""second""); + "); + var fixedCode = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ 1, 2, 3 }}; + Assert.That(collection, {CollectionAssertUsageAnalyzer.OneCollectionParameterAsserts[method]}, $""{{""first""}}, {{""second""}}""); + "); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); + } + + [TestCaseSource(nameof(OneCollectionParameterAsserts))] + public void AnalyzeOneCollectionWhenFormatAndParamsArgumentsAreUsedOutOfOrder(string method) + { + var code = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ 1, 2, 3 }}; + ↓CollectionAssert.{method}(args: new[] {{ ""first"", ""second"" }}, message: ""{{0}}, {{1}}"", collection: collection); + "); + var fixedCode = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ 1, 2, 3 }}; + Assert.That(collection, {CollectionAssertUsageAnalyzer.OneCollectionParameterAsserts[method]}, $""{{""first""}}, {{""second""}}""); + "); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); + } + [TestCase(NUnitFrameworkConstants.NameOfCollectionAssertIsOrdered)] public void AnalyzeOneCollectionWithComparerWhenFormatAndParamsArgumentsAreUsed(string method) { @@ -96,7 +124,7 @@ public void AnalyzeTwoCollectionWhenNoMessageArgumentsAreUsed(string method) } [TestCaseSource(nameof(TwoCollectionParameterAsserts))] - public void AnalyzeTwoCollectionWhenOnlyMessageArgumentsAreUsed(string method) + public void AnalyzeTwoCollectionWhenOnlyMessageArgumentIsUsed(string method) { var code = TestUtility.WrapInTestMethod(@$" var collection1 = new[] {{ 1, 2, 3 }}; @@ -112,7 +140,7 @@ public void AnalyzeTwoCollectionWhenOnlyMessageArgumentsAreUsed(string method) } [TestCaseSource(nameof(TwoCollectionParameterAsserts))] - public void AnalyzeTwoCollectionWhenFormatAndParamsArgumentsAreUsed(string method) + public void AnalyzeTwoCollectionWhenFormatAndOneParamsArgumentAreUsed(string method) { var code = TestUtility.WrapInTestMethod(@$" var collection1 = new[] {{ 1, 2, 3 }}; @@ -127,6 +155,40 @@ public void AnalyzeTwoCollectionWhenFormatAndParamsArgumentsAreUsed(string metho RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); } + [TestCaseSource(nameof(TwoCollectionParameterAsserts))] + public void AnalyzeTwoCollectionWhenFormatAndTwoParamsArgumentsAreUsed(string method) + { + var code = TestUtility.WrapInTestMethod(@$" + var collection1 = new[] {{ 1, 2, 3 }}; + var collection2 = new[] {{ 2, 4, 6 }}; + ↓CollectionAssert.{method}(collection1, collection2, ""{{0}}, {{1}}"", ""first"", ""second""); + "); + var fixedCode = TestUtility.WrapInTestMethod(@$" + var collection1 = new[] {{ 1, 2, 3 }}; + var collection2 = new[] {{ 2, 4, 6 }}; + Assert.That({GetAdjustedTwoCollectionConstraint(method)}, $""{{""first""}}, {{""second""}}""); + "); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); + } + + [TestCaseSource(nameof(TwoCollectionParameterAsserts))] + public void AnalyzeTwoCollectionWhenFormatAndParamsArgumentsAreUsedOutOfOrder(string method) + { + var firstParameterName = CollectionAssertUsageCodeFix.CollectionAssertToFirstParameterName[method]; + var secondParameterName = CollectionAssertUsageCodeFix.CollectionAssertToSecondParameterName[method]; + var code = TestUtility.WrapInTestMethod(@$" + var collection1 = new[] {{ 1, 2, 3 }}; + var collection2 = new[] {{ 2, 4, 6 }}; + ↓CollectionAssert.{method}(args: new[] {{ ""first"", ""second"" }}, message: ""{{0}}, {{1}}"", {secondParameterName}: collection2, {firstParameterName}: collection1); + "); + var fixedCode = TestUtility.WrapInTestMethod(@$" + var collection1 = new[] {{ 1, 2, 3 }}; + var collection2 = new[] {{ 2, 4, 6 }}; + Assert.That({GetAdjustedTwoCollectionConstraint(method)}, $""{{""first""}}, {{""second""}}""); + "); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); + } + [TestCase(NUnitFrameworkConstants.NameOfCollectionAssertAreEqual)] [TestCase(NUnitFrameworkConstants.NameOfCollectionAssertAreNotEqual)] public void AnalyzeTwoCollectionWithComparerWhenFormatAndParamsArgumentsAreUsed(string method) @@ -163,7 +225,7 @@ public void AnalyzeCollectionAndItemWhenNoMessageArgumentsAreUsed(string method) } [TestCaseSource(nameof(CollectionAndItemParameterAsserts))] - public void AnalyzeCollectionAndItemWhenOnlyMessageArgumentsAreUsed(string method) + public void AnalyzeCollectionAndItemWhenOnlyMessageArgumentIsUsed(string method) { var code = TestUtility.WrapInTestMethod(@$" var collection = new[] {{ typeof(byte), typeof(char) }}; @@ -179,7 +241,7 @@ public void AnalyzeCollectionAndItemWhenOnlyMessageArgumentsAreUsed(string metho } [TestCaseSource(nameof(CollectionAndItemParameterAsserts))] - public void AnalyzeCollectionAndItemWhenFormatAndParamsArgumentsAreUsed(string method) + public void AnalyzeCollectionAndItemWhenFormatAndOneParamsArgumentAreUsed(string method) { var code = TestUtility.WrapInTestMethod(@$" var collection = new[] {{ typeof(byte), typeof(char) }}; @@ -194,21 +256,45 @@ public void AnalyzeCollectionAndItemWhenFormatAndParamsArgumentsAreUsed(string m RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); } + [TestCaseSource(nameof(CollectionAndItemParameterAsserts))] + public void AnalyzeCollectionAndItemWhenFormatAndTwoParamsArgumentsAreUsed(string method) + { + var code = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ typeof(byte), typeof(char) }}; + var expected = typeof(byte); + ↓CollectionAssert.{method}(collection, expected, ""{{0}}, {{1}}"", ""first"", ""second""); + "); + var fixedCode = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ typeof(byte), typeof(char) }}; + var expected = typeof(byte); + Assert.That(collection, {CollectionAssertUsageAnalyzer.CollectionAndItemParameterAsserts[method]}, $""{{""first""}}, {{""second""}}""); + "); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); + } + + [TestCaseSource(nameof(CollectionAndItemParameterAsserts))] + public void AnalyzeCollectionAndItemWhenFormatAndParamsArgumentsAreUsedOutOfOrder(string method) + { + var secondParameterName = CollectionAssertUsageCodeFix.CollectionAssertToSecondParameterName[method]; + var code = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ typeof(byte), typeof(char) }}; + var expected = typeof(byte); + ↓CollectionAssert.{method}(args: new[] {{ ""first"", ""second"" }}, message: ""{{0}}, {{1}}"", {secondParameterName}: expected, collection: collection); + "); + var fixedCode = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ typeof(byte), typeof(char) }}; + var expected = typeof(byte); + Assert.That(collection, {CollectionAssertUsageAnalyzer.CollectionAndItemParameterAsserts[method]}, $""{{""first""}}, {{""second""}}""); + "); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); + } + private static string GetAdjustedTwoCollectionConstraint(string method) { - string actualArgument; - string constraintArgument; - - if (CollectionAssertUsageCodeFix.CollectionAssertToOneUnswappedParameterConstraints.ContainsKey(method)) - { - actualArgument = "collection1"; - constraintArgument = "collection2"; - } - else - { - actualArgument = "collection2"; - constraintArgument = "collection1"; - } + (string actualArgument, string constraintArgument) = + CollectionAssertUsageCodeFix.CollectionAssertToOneUnswappedParameterConstraints.ContainsKey(method) + ? ("collection1", "collection2") + : ("collection2", "collection1"); string constraint = CollectionAssertUsageAnalyzer.TwoCollectionParameterAsserts[method] .Replace("expected", constraintArgument); diff --git a/src/nunit.analyzers.tests/StringAssertUsage/StringAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/StringAssertUsage/StringAssertUsageCodeFixTests.cs index 92b4c161..6e65380d 100644 --- a/src/nunit.analyzers.tests/StringAssertUsage/StringAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/StringAssertUsage/StringAssertUsageCodeFixTests.cs @@ -53,10 +53,19 @@ public void AnalyzeWhenFormatAndArgumentsAreUsed(string method) RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); } - private static string GetAdjustedConstraint(string method) + [TestCaseSource(nameof(StringAsserts))] + public void AnalyzeWhenFormatAndArgumentsAreUsedOutOfOrder(string method) { - return StringAssertUsageAnalyzer.StringAssertToConstraint[method] - .Replace("expected", "\"expected\""); + var firstParameterName = StringAssertUsageCodeFix.StringAssertToExpectedParameterName[method]; + var code = TestUtility.WrapInTestMethod(@$" + ↓StringAssert.{method}(args: new[] {{ ""first"", ""second"" }}, message: ""{{0}}, {{1}}"", actual: ""actual"", {firstParameterName}: ""expected"");"); + var fixedCode = TestUtility.WrapInTestMethod(@$" + Assert.That(""actual"", {GetAdjustedConstraint(method)}, $""{{""first""}}, {{""second""}}"");"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); } + + private static string GetAdjustedConstraint(string method) => + StringAssertUsageAnalyzer.StringAssertToConstraint[method] + .Replace("expected", "\"expected\""); } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFix.cs index 2c0b7105..fc46671b 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFix.cs @@ -16,25 +16,25 @@ public sealed class AreEqualClassicModelAssertUsageCodeFix { public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(AnalyzerIdentifiers.AreEqualUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - // Note that if there's a 3rd argument and it's a double, - // it has to be added to the "Is.EqualTo(1st argument)" with ".Within(3rd argument)" + var expectedArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfExpectedParameter].WithNameColon(null); var equalToInvocationNode = SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsEqualTo))) .WithArgumentList(SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList(arguments[0]))); + SyntaxFactory.SingletonSeparatedList(expectedArgument))); - var hasToleranceValue = diagnostic.Properties[AnalyzerPropertyKeys.HasToleranceValue] == true.ToString(); - - if (hasToleranceValue) + // The tolerance argument has to be added to the "Is.EqualTo(expected)" as ".Within(tolerance)" + if (argumentNamesToArguments.TryGetValue(NUnitFrameworkConstants.NameOfDeltaParameter, out var toleranceArgument)) { // The tolerance argument should be renamed from 'delta' to 'amount' but with the model constraint the // argument is moved to Within which makes it way more explicit so we can just drop the name colon. - var toleranceArgumentNoColon = arguments[2].WithNameColon(null); + var toleranceArgumentNoColon = toleranceArgument.WithNameColon(null); equalToInvocationNode = SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( @@ -45,16 +45,8 @@ protected override void UpdateArguments(Diagnostic diagnostic, List FixableDiagnosticIds { get; } = ImmutableArray.Create(AnalyzerIdentifiers.AreNotEqualUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - arguments.Insert(2, SyntaxFactory.Argument( + var expectedArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfExpectedParameter].WithNameColon(null); + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, @@ -28,10 +31,10 @@ protected override void UpdateArguments(Diagnostic diagnostic, List FixableDiagnosticIds { get; } = ImmutableArray.Create(AnalyzerIdentifiers.AreNotSameUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - arguments.Insert(2, SyntaxFactory.Argument( + var expectedArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfExpectedParameter].WithNameColon(null); + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, @@ -28,10 +31,10 @@ protected override void UpdateArguments(Diagnostic diagnostic, List FixableDiagnosticIds { get; } = ImmutableArray.Create(AnalyzerIdentifiers.AreSameUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - arguments.Insert(2, SyntaxFactory.Argument( + var expectedArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfExpectedParameter].WithNameColon(null); + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsSameAs))) .WithArgumentList(SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList(arguments[0]))))); + SyntaxFactory.SingletonSeparatedList(expectedArgument)))); - // Then we have to remove the 1st argument because that's now in the "Is.SameAs()" - arguments.RemoveAt(0); + var actualArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfActualParameter].WithNameColon(null); + return (actualArgument, constraintArgument); } } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/ClassicModelAssertUsageAnalyzer.cs b/src/nunit.analyzers/ClassicModelAssertUsage/ClassicModelAssertUsageAnalyzer.cs index 60213141..c2d479b3 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/ClassicModelAssertUsageAnalyzer.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/ClassicModelAssertUsageAnalyzer.cs @@ -252,10 +252,6 @@ protected override void AnalyzeAssertInvocation(OperationAnalysisContext context return new Dictionary { [AnalyzerPropertyKeys.ModelName] = invocationSymbol.Name, - [AnalyzerPropertyKeys.HasToleranceValue] = - (invocationSymbol.Name == NameOfAssertAreEqual && - invocationSymbol.Parameters.Length >= 3 && - invocationSymbol.Parameters[2].Type.SpecialType == SpecialType.System_Double).ToString(), }.ToImmutableDictionary(); } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/ClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/ClassicModelAssertUsageCodeFix.cs index 881c679d..5eb68206 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/ClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/ClassicModelAssertUsageCodeFix.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using NUnit.Analyzers.Constants; using NUnit.Analyzers.Extensions; using NUnit.Analyzers.Helpers; @@ -40,42 +41,41 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var diagnostic = context.Diagnostics.First(); var node = root.FindNode(diagnostic.Location.SourceSpan); var invocationNode = node as InvocationExpressionSyntax; - if (invocationNode is null) return; var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + if (semanticModel is null) + return; // Replace the original ClassicAssert. invocation name into Assert.That var newInvocationNode = invocationNode.UpdateClassicAssertToAssertThat(out TypeArgumentListSyntax? typeArguments); - if (newInvocationNode is null) return; - // Now, replace the arguments. - List arguments = invocationNode.ArgumentList.Arguments.ToList(); + var methodSymbol = semanticModel.GetSymbolInfo(invocationNode).Symbol as IMethodSymbol; + if (methodSymbol is null) + return; - // See if we need to cast the arguments when they were using a specific classic overload. - arguments[0] = CastIfNecessary(arguments[0]); - if (arguments.Count > 1) - arguments[1] = CastIfNecessary(arguments[1]); + var (argumentNamesToArguments, args) = SplitUpOtherParametersAndParamParameter(methodSymbol, invocationNode); - // Do the rule specific conversion - if (typeArguments is null) - this.UpdateArguments(diagnostic, arguments); - else - this.UpdateArguments(diagnostic, arguments, typeArguments); + // Now, replace the arguments. + List newArguments = new(); - // Remove null message to avoid ambiguous calls. - if (arguments.Count == 3 && arguments[2].Expression.IsKind(SyntaxKind.NullLiteralExpression)) - { - arguments.RemoveAt(2); - } + // Do the rule specific conversion + var (actualArgument, constraintArgument) = typeArguments is null + ? this.ConstructActualAndConstraintArguments(diagnostic, argumentNamesToArguments) + : this.ConstructActualAndConstraintArguments(diagnostic, argumentNamesToArguments, typeArguments); + newArguments.Add(actualArgument); + if (constraintArgument is not null) + newArguments.Add(constraintArgument); // Do the format spec, params to formattable string conversion - CodeFixHelper.UpdateStringFormatToFormattableString(arguments, this.MinimumNumberOfParameters); + argumentNamesToArguments.TryGetValue(NUnitFrameworkConstants.NameOfMessageParameter, out ArgumentSyntax? messageArgument); + if (CodeFixHelper.GetInterpolatedMessageArgumentOrDefault(messageArgument, args) is ArgumentSyntax interpolatedMessageArgument) + newArguments.Add(interpolatedMessageArgument); - var newArgumentsList = invocationNode.ArgumentList.WithArguments(arguments); + var newArgumentsList = invocationNode.ArgumentList.WithArguments(newArguments); newInvocationNode = newInvocationNode.WithArgumentList(newArgumentsList); context.CancellationToken.ThrowIfCancellationRequested(); @@ -86,11 +86,43 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) CodeAction.Create( this.Title, _ => Task.FromResult(context.Document.WithSyntaxRoot(newRoot)), - this.Title), diagnostic); + this.Title), + diagnostic); + + (Dictionary argumentNamesToArguments, List args) SplitUpOtherParametersAndParamParameter( + IMethodSymbol methodSymbol, + InvocationExpressionSyntax invocationNode) + { + Dictionary argumentNamesToArguments = new(); + + // There can be 0 to any number of arguments mapped to args. + List args = new(); + + var arguments = invocationNode.ArgumentList.Arguments.ToList(); + for (var i = 0; i < arguments.Count; ++i) + { + var argument = arguments[i]; + if (i < methodSymbol.Parameters.Length + && (argument.NameColon?.Name.Identifier.Text ?? methodSymbol.Parameters[i].Name) is string argumentName + && argumentName != NUnitFrameworkConstants.NameOfArgsParameter) + { + // See if we need to cast the arguments when they were using a specific classic overload. + argumentNamesToArguments[argumentName] = argumentName is NUnitFrameworkConstants.NameOfMessageParameter + ? argument + : CastIfNecessary(argument); + } + else + { + args.Add(argument); + } + } + + return (argumentNamesToArguments, args); + } ArgumentSyntax CastIfNecessary(ArgumentSyntax argument) { - string? implicitTypeConversion = GetUserDefinedImplicitTypeConversion(argument.Expression); + string? implicitTypeConversion = GetUserDefinedImplicitTypeConversionOrDefault(argument.Expression); if (implicitTypeConversion is null) return argument; @@ -101,7 +133,7 @@ ArgumentSyntax CastIfNecessary(ArgumentSyntax argument) argument.Expression)); } - string? GetUserDefinedImplicitTypeConversion(ExpressionSyntax expression) + string? GetUserDefinedImplicitTypeConversionOrDefault(ExpressionSyntax expression) { var typeInfo = semanticModel.GetTypeInfo(expression, context.CancellationToken); var convertedType = typeInfo.ConvertedType; @@ -121,14 +153,16 @@ ArgumentSyntax CastIfNecessary(ArgumentSyntax argument) } } - protected virtual void UpdateArguments(Diagnostic diagnostic, List arguments, TypeArgumentListSyntax typeArguments) - { - throw new InvalidOperationException($"Class must override {nameof(UpdateArguments)} accepting {nameof(TypeArgumentListSyntax)}"); - } - - protected virtual void UpdateArguments(Diagnostic diagnostic, List arguments) - { - throw new InvalidOperationException($"Class must override {nameof(UpdateArguments)}"); - } + // ConstraintArgument is nullable because Assert.True and Assert.IsTrue are transformed into Assert.That without a constraint. + protected virtual (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) => + throw new NotImplementedException($"Class must override {nameof(ConstructActualAndConstraintArguments)}"); + + protected virtual (ArgumentSyntax ActualArgument, ArgumentSyntax ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments, + TypeArgumentListSyntax typeArguments) => + throw new InvalidOperationException($"Class must override {nameof(ConstructActualAndConstraintArguments)} accepting {nameof(TypeArgumentListSyntax)}"); } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/ContainsClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/ContainsClassicModelAssertUsageCodeFix.cs index c0a2f24d..94876d81 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/ContainsClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/ContainsClassicModelAssertUsageCodeFix.cs @@ -16,19 +16,22 @@ public sealed class ContainsClassicModelAssertUsageCodeFix { public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(AnalyzerIdentifiers.ContainsUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - arguments.Insert(2, SyntaxFactory.Argument( + var expectedArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfExpectedParameter].WithNameColon(null); + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfDoes), SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfDoesContain))) .WithArgumentList(SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList(arguments[0]))))); + SyntaxFactory.SingletonSeparatedList(expectedArgument)))); - // Then we have to remove the 1st argument because that's now in the "Does.Contain()" - arguments.RemoveAt(0); + var actualArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfActualParameter].WithNameColon(null); + return (actualArgument, constraintArgument); } } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/GreaterClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/GreaterClassicModelAssertUsageCodeFix.cs index 86598365..2045d853 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/GreaterClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/GreaterClassicModelAssertUsageCodeFix.cs @@ -16,19 +16,22 @@ public sealed class GreaterClassicModelAssertUsageCodeFix { public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(AnalyzerIdentifiers.GreaterUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - arguments.Insert(2, SyntaxFactory.Argument( + var arg2Argument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfArg2Parameter].WithNameColon(null); + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsGreaterThan))) .WithArgumentList(SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList(arguments[1]))))); + SyntaxFactory.SingletonSeparatedList(arg2Argument)))); - // Then we have to remove the 2nd argument because that's now in the "Is.GreaterThan()" - arguments.RemoveAt(1); + var arg1Argument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfArg1Parameter].WithNameColon(null); + return (arg1Argument, constraintArgument); } } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/GreaterOrEqualClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/GreaterOrEqualClassicModelAssertUsageCodeFix.cs index d6dc6a44..0fab1795 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/GreaterOrEqualClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/GreaterOrEqualClassicModelAssertUsageCodeFix.cs @@ -16,19 +16,22 @@ public sealed class GreaterOrEqualClassicModelAssertUsageCodeFix { public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(AnalyzerIdentifiers.GreaterOrEqualUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - arguments.Insert(2, SyntaxFactory.Argument( + var arg2Argument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfArg2Parameter].WithNameColon(null); + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsGreaterThanOrEqualTo))) .WithArgumentList(SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList(arguments[1]))))); + SyntaxFactory.SingletonSeparatedList(arg2Argument)))); - // Then we have to remove the 2nd argument because that's now in the "Is.GreaterThanOrEqualTo()" - arguments.RemoveAt(1); + var arg1Argument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfArg1Parameter].WithNameColon(null); + return (arg1Argument, constraintArgument); } } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/IsEmptyClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/IsEmptyClassicModelAssertUsageCodeFix.cs index 4ab7f095..3adb8b76 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/IsEmptyClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/IsEmptyClassicModelAssertUsageCodeFix.cs @@ -17,13 +17,22 @@ public sealed class IsEmptyClassicModelAssertUsageCodeFix public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create( AnalyzerIdentifiers.IsEmptyUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - arguments.Insert(1, SyntaxFactory.Argument( + // different overloads have different "actual" arguments + var actualArgument = argumentNamesToArguments.TryGetValue(NUnitFrameworkConstants.NameOfCollectionParameter, out var collectionArgument) + ? collectionArgument + : argumentNamesToArguments[NUnitFrameworkConstants.NameOfAStringParameter]; + actualArgument = actualArgument.WithNameColon(null); + + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), - SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsEmpty)))); + SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsEmpty))); + return (actualArgument, constraintArgument); } } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/IsFalseAndFalseClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/IsFalseAndFalseClassicModelAssertUsageCodeFix.cs index 5cf2113d..e52debad 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/IsFalseAndFalseClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/IsFalseAndFalseClassicModelAssertUsageCodeFix.cs @@ -18,13 +18,17 @@ public sealed class IsFalseAndFalseClassicModelAssertUsageCodeFix AnalyzerIdentifiers.IsFalseUsage, AnalyzerIdentifiers.FalseUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - arguments.Insert(1, SyntaxFactory.Argument( + var actualArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfConditionParameter].WithNameColon(null); + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), - SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsFalse)))); + SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsFalse))); + return (actualArgument, constraintArgument); } } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/IsInstanceOfClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/IsInstanceOfClassicModelAssertUsageCodeFix.cs index 66d1e857..dc3a59ae 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/IsInstanceOfClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/IsInstanceOfClassicModelAssertUsageCodeFix.cs @@ -16,30 +16,38 @@ public sealed class IsInstanceOfClassicModelAssertUsageCodeFix { public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(AnalyzerIdentifiers.IsInstanceOfUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments, TypeArgumentListSyntax typeArguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments, + TypeArgumentListSyntax typeArguments) { - arguments.Insert(1, SyntaxFactory.Argument( + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), SyntaxFactory.GenericName(NUnitFrameworkConstants.NameOfIsInstanceOf) - .WithTypeArgumentList(typeArguments))))); + .WithTypeArgumentList(typeArguments)))); + var actualArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfActualParameter].WithNameColon(null); + return (actualArgument, constraintArgument); } - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - arguments.Insert(2, SyntaxFactory.Argument( + var expectedArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfExpectedParameter].WithNameColon(null); + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsInstanceOf))) .WithArgumentList(SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList(arguments[0]))))); + SyntaxFactory.SingletonSeparatedList(expectedArgument)))); - // Then we have to remove the 1st argument because that's now in the "Is.InstanceOf()" - arguments.RemoveAt(0); + var actualArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfActualParameter].WithNameColon(null); + return (actualArgument, constraintArgument); } } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/IsNaNClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/IsNaNClassicModelAssertUsageCodeFix.cs index 5144ae7d..81295efc 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/IsNaNClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/IsNaNClassicModelAssertUsageCodeFix.cs @@ -17,13 +17,17 @@ public sealed class IsNaNClassicModelAssertUsageCodeFix public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create( AnalyzerIdentifiers.IsNaNUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - arguments.Insert(1, SyntaxFactory.Argument( + var actualArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfADoubleParameter].WithNameColon(null); + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), - SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsNaN)))); + SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsNaN))); + return (actualArgument, constraintArgument); } } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/IsNotEmptyClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/IsNotEmptyClassicModelAssertUsageCodeFix.cs index edfb8cc2..3f0a7724 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/IsNotEmptyClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/IsNotEmptyClassicModelAssertUsageCodeFix.cs @@ -17,16 +17,24 @@ public sealed class IsNotEmptyClassicModelAssertUsageCodeFix public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create( AnalyzerIdentifiers.IsNotEmptyUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - arguments.Insert(1, SyntaxFactory.Argument( + var actualArgument = argumentNamesToArguments.TryGetValue(NUnitFrameworkConstants.NameOfCollectionParameter, out var collectionArgument) + ? collectionArgument + : argumentNamesToArguments[NUnitFrameworkConstants.NameOfAStringParameter]; + actualArgument = actualArgument.WithNameColon(null); + + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsNot)), - SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsEmpty)))); + SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsEmpty))); + return (actualArgument, constraintArgument); } } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/IsNotInstanceOfClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/IsNotInstanceOfClassicModelAssertUsageCodeFix.cs index d6e05367..39cb57c1 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/IsNotInstanceOfClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/IsNotInstanceOfClassicModelAssertUsageCodeFix.cs @@ -16,9 +16,12 @@ public sealed class IsNotInstanceOfClassicModelAssertUsageCodeFix { public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(AnalyzerIdentifiers.IsNotInstanceOfUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments, TypeArgumentListSyntax typeArguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments, + TypeArgumentListSyntax typeArguments) { - arguments.Insert(1, SyntaxFactory.Argument( + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, @@ -27,12 +30,17 @@ protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - arguments.Insert(2, SyntaxFactory.Argument( + var expectedArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfExpectedParameter].WithNameColon(null); + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, @@ -42,10 +50,10 @@ protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - arguments.Insert(1, SyntaxFactory.Argument( + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsNot)), - SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsNull)))); + SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsNull))); + + var actualArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfAnObjectParameter].WithNameColon(null); + return (actualArgument, constraintArgument); } } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/IsNullAndNullClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/IsNullAndNullClassicModelAssertUsageCodeFix.cs index 0df23b15..908ee2cb 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/IsNullAndNullClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/IsNullAndNullClassicModelAssertUsageCodeFix.cs @@ -18,13 +18,18 @@ public sealed class IsNullAndNullClassicModelAssertUsageCodeFix AnalyzerIdentifiers.IsNullUsage, AnalyzerIdentifiers.NullUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - arguments.Insert(1, SyntaxFactory.Argument( + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), - SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsNull)))); + SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsNull))); + + var actualArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfAnObjectParameter].WithNameColon(null); + return (actualArgument, constraintArgument); } } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCodeFix.cs index 1bf9e10f..2eb25e67 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCodeFix.cs @@ -18,13 +18,18 @@ public sealed class IsTrueAndTrueClassicModelAssertUsageCodeFix AnalyzerIdentifiers.IsTrueUsage, AnalyzerIdentifiers.TrueUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - arguments.Insert(1, SyntaxFactory.Argument( + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), - SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsTrue)))); + SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsTrue))); + + var actualArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfConditionParameter].WithNameColon(null); + return (actualArgument, constraintArgument); } } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCondensedCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCondensedCodeFix.cs index e37f7f9c..e2401aba 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCondensedCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCondensedCodeFix.cs @@ -23,9 +23,12 @@ public sealed class IsTrueAndTrueClassicModelAssertUsageCondensedCodeFix protected override string Title => base.Title + Suffix; - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - // Nothing to do + var actualArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfConditionParameter].WithNameColon(null); + return (actualArgument, null); // The condensed form doesn't have the constraint argument. } } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/LessClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/LessClassicModelAssertUsageCodeFix.cs index 70ae561d..e0cb12ca 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/LessClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/LessClassicModelAssertUsageCodeFix.cs @@ -16,19 +16,22 @@ public sealed class LessClassicModelAssertUsageCodeFix { public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(AnalyzerIdentifiers.LessUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - arguments.Insert(2, SyntaxFactory.Argument( + var arg2Argument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfArg2Parameter].WithNameColon(null); + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsLessThan))) .WithArgumentList(SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList(arguments[1]))))); + SyntaxFactory.SingletonSeparatedList(arg2Argument)))); - // Then we have to remove the 2nd argument because that's now in the "Is.LessThan()" - arguments.RemoveAt(1); + var arg1Argument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfArg1Parameter].WithNameColon(null); + return (arg1Argument, constraintArgument); } } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/LessOrEqualClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/LessOrEqualClassicModelAssertUsageCodeFix.cs index ca3f85e7..4c69a932 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/LessOrEqualClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/LessOrEqualClassicModelAssertUsageCodeFix.cs @@ -16,19 +16,22 @@ public sealed class LessOrEqualClassicModelAssertUsageCodeFix { public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(AnalyzerIdentifiers.LessOrEqualUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - arguments.Insert(2, SyntaxFactory.Argument( + var arg2Argument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfArg2Parameter].WithNameColon(null); + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsLessThanOrEqualTo))) .WithArgumentList(SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList(arguments[1]))))); + SyntaxFactory.SingletonSeparatedList(arg2Argument)))); - // Then we have to remove the 2nd argument because that's now in the "Is.LessThanOrEqualTo()" - arguments.RemoveAt(1); + var arg1Argument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfArg1Parameter].WithNameColon(null); + return (arg1Argument, constraintArgument); } } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/NotZeroClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/NotZeroClassicModelAssertUsageCodeFix.cs index 0069865b..c76db4cf 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/NotZeroClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/NotZeroClassicModelAssertUsageCodeFix.cs @@ -17,16 +17,20 @@ public sealed class NotZeroClassicModelAssertUsageCodeFix public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create( AnalyzerIdentifiers.NotZeroUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - arguments.Insert(1, SyntaxFactory.Argument( + var actualArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfActualParameter].WithNameColon(null); + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsNot)), - SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsZero)))); + SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsZero))); + return (actualArgument, constraintArgument); } } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/ZeroClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/ZeroClassicModelAssertUsageCodeFix.cs index cb4e7591..4eb547d9 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/ZeroClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/ZeroClassicModelAssertUsageCodeFix.cs @@ -17,13 +17,17 @@ public sealed class ZeroClassicModelAssertUsageCodeFix public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create( AnalyzerIdentifiers.ZeroUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - arguments.Insert(1, SyntaxFactory.Argument( + var actualArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfActualParameter].WithNameColon(null); + var constraintArgument = SyntaxFactory.Argument( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), - SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsZero)))); + SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsZero))); + return (actualArgument, constraintArgument); } } } diff --git a/src/nunit.analyzers/CollectionAssertUsage/CollectionAssertUsageAnalyzer.cs b/src/nunit.analyzers/CollectionAssertUsage/CollectionAssertUsageAnalyzer.cs index 6cd1bb4a..b4504399 100644 --- a/src/nunit.analyzers/CollectionAssertUsage/CollectionAssertUsageAnalyzer.cs +++ b/src/nunit.analyzers/CollectionAssertUsage/CollectionAssertUsageAnalyzer.cs @@ -67,26 +67,16 @@ protected override void AnalyzeAssertInvocation(OperationAnalysisContext context TwoCollectionParameterAsserts.TryGetValue(methodSymbol.Name, out constraint) || CollectionAndItemParameterAsserts.TryGetValue(methodSymbol.Name, out constraint)) { - var parameters = methodSymbol.Parameters; - string comparerParameterIndex = parameters.Length > 1 && IsIComparer(parameters[1]) ? "1" : - (parameters.Length > 2 && IsIComparer(parameters[2]) ? "2" : "0"); - context.ReportDiagnostic(Diagnostic.Create( collectionAssertDescriptor, assertOperation.Syntax.GetLocation(), new Dictionary { [AnalyzerPropertyKeys.ModelName] = methodSymbol.Name, - [AnalyzerPropertyKeys.ComparerParameterIndex] = comparerParameterIndex, }.ToImmutableDictionary(), constraint, methodSymbol.Name)); } } - - private static bool IsIComparer(IParameterSymbol parameterSymbol) - { - return parameterSymbol.Type.Name == "IComparer"; - } } } diff --git a/src/nunit.analyzers/CollectionAssertUsage/CollectionAssertUsageCodeFix.cs b/src/nunit.analyzers/CollectionAssertUsage/CollectionAssertUsageCodeFix.cs index cd932db5..043bc667 100644 --- a/src/nunit.analyzers/CollectionAssertUsage/CollectionAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/CollectionAssertUsage/CollectionAssertUsageCodeFix.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; @@ -48,47 +49,55 @@ internal class CollectionAssertUsageCodeFix : ClassicModelAssertUsageCodeFix { NameOfCollectionAssertIsSupersetOf, new Constraints(NameOfIs, default(string), NameOfIsSupersetOf) }, }.ToImmutableDictionary(); - public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create( - AnalyzerIdentifiers.CollectionAssertUsage); - - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) - { - string methodName = diagnostic.Properties[AnalyzerPropertyKeys.ModelName]!; - - int comparerParameterIndex = diagnostic.Properties[AnalyzerPropertyKeys.ComparerParameterIndex] switch + internal static readonly ImmutableDictionary CollectionAssertToFirstParameterName = + new Dictionary() { - "2" => 2, - "1" => 1, - _ => 0, - }; + { NameOfCollectionAssertAllItemsAreNotNull, NameOfCollectionParameter }, + { NameOfCollectionAssertAllItemsAreUnique, NameOfCollectionParameter }, + { NameOfCollectionAssertIsEmpty, NameOfCollectionParameter }, + { NameOfCollectionAssertIsNotEmpty, NameOfCollectionParameter }, + { NameOfCollectionAssertIsOrdered, NameOfCollectionParameter }, + { NameOfCollectionAssertAreEqual, NameOfExpectedParameter }, + { NameOfCollectionAssertAreEquivalent, NameOfExpectedParameter }, + { NameOfCollectionAssertAreNotEqual, NameOfExpectedParameter }, + { NameOfCollectionAssertAreNotEquivalent, NameOfExpectedParameter }, + { NameOfCollectionAssertAllItemsAreInstancesOfType, NameOfCollectionParameter }, + { NameOfCollectionAssertContains, NameOfCollectionParameter }, + { NameOfCollectionAssertDoesNotContain, NameOfCollectionParameter }, + { NameOfCollectionAssertIsNotSubsetOf, NameOfSubsetParameter }, + { NameOfCollectionAssertIsSubsetOf, NameOfSubsetParameter }, + { NameOfCollectionAssertIsNotSupersetOf, NameOfSupersetParameter }, + { NameOfCollectionAssertIsSupersetOf, NameOfSupersetParameter }, + }.ToImmutableDictionary(); - ArgumentSyntax? comparerArgument = null; - if (comparerParameterIndex > 0) + internal static readonly ImmutableDictionary CollectionAssertToSecondParameterName = + new Dictionary() { - // Remember 'comparer' parameter to be added as an 'Using' suffix. - comparerArgument = arguments[comparerParameterIndex]; - arguments.RemoveAt(comparerParameterIndex); - } + { NameOfCollectionAssertAreEqual, NameOfActualParameter }, + { NameOfCollectionAssertAreEquivalent, NameOfActualParameter }, + { NameOfCollectionAssertAreNotEqual, NameOfActualParameter }, + { NameOfCollectionAssertAreNotEquivalent, NameOfActualParameter }, + { NameOfCollectionAssertAllItemsAreInstancesOfType, NameOfExpectedTypeParameter }, + { NameOfCollectionAssertContains, NameOfActualParameter }, + { NameOfCollectionAssertDoesNotContain, NameOfActualParameter }, + { NameOfCollectionAssertIsNotSubsetOf, NameOfSupersetParameter }, + { NameOfCollectionAssertIsSubsetOf, NameOfSupersetParameter }, + { NameOfCollectionAssertIsNotSupersetOf, NameOfSubsetParameter }, + { NameOfCollectionAssertIsSupersetOf, NameOfSubsetParameter }, + }.ToImmutableDictionary(); - if (CollectionAssertToParameterlessConstraints.TryGetValue(methodName, out Constraints? constraints)) - { - arguments.Insert(1, Argument(constraints.CreateConstraint())); - } - else if (CollectionAssertToOneSwappedParameterConstraints.TryGetValue(methodName, out constraints)) - { - arguments.Insert(2, Argument(constraints.CreateConstraint(arguments[0]))); + public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create( + AnalyzerIdentifiers.CollectionAssertUsage); - // Then we have to remove the 1st argument because that's now in the "constaint" - arguments.RemoveAt(0); - } - else if (CollectionAssertToOneUnswappedParameterConstraints.TryGetValue(methodName, out constraints)) - { - arguments[1] = Argument(constraints.CreateConstraint(arguments[1])); - } + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) + { + var (actualArgument, constraintArgument) = GetActualAndConstraintArguments(diagnostic, argumentNamesToArguments); - if (comparerArgument is not null) + if (argumentNamesToArguments.TryGetValue(NameOfComparerParameter, out ArgumentSyntax? comparerArgument)) { - ExpressionSyntax expression = arguments[1].Expression; + ExpressionSyntax expression = constraintArgument.Expression; // We need to drop the 'AsCollection' when using an IComparer. if (expression is MemberAccessExpressionSyntax memberAccessExpression && @@ -97,7 +106,7 @@ protected override void UpdateArguments(Diagnostic diagnostic, List argumentNamesToArguments) + { + var methodName = diagnostic.Properties[AnalyzerPropertyKeys.ModelName]!; + var firstParameterName = CollectionAssertToFirstParameterName[methodName]; + var firstArgument = argumentNamesToArguments[firstParameterName].WithNameColon(null); + + if (CollectionAssertToParameterlessConstraints.TryGetValue(methodName, out Constraints? constraints)) + { + var constraintArgument = Argument(constraints.CreateConstraint()); + return (firstArgument, constraintArgument); + } + else if (CollectionAssertToOneSwappedParameterConstraints.TryGetValue(methodName, out constraints)) + { + var secondParameterName = CollectionAssertToSecondParameterName[methodName]; + var secondArgument = argumentNamesToArguments[secondParameterName].WithNameColon(null); + var constraintArgument = Argument(constraints.CreateConstraint(firstArgument)); + return (secondArgument, constraintArgument); + } + else if (CollectionAssertToOneUnswappedParameterConstraints.TryGetValue(methodName, out constraints)) + { + var secondParameterName = CollectionAssertToSecondParameterName[methodName]; + var secondArgument = argumentNamesToArguments[secondParameterName].WithNameColon(null); + var constraintArgument = Argument(constraints.CreateConstraint(secondArgument)); + + return (firstArgument, constraintArgument); + } + else + { + throw new InvalidOperationException($"Unknown method name: {methodName}"); + } } } } diff --git a/src/nunit.analyzers/Constants/AnalyzerPropertyKeys.cs b/src/nunit.analyzers/Constants/AnalyzerPropertyKeys.cs index 0a4e1056..10b5bdc0 100644 --- a/src/nunit.analyzers/Constants/AnalyzerPropertyKeys.cs +++ b/src/nunit.analyzers/Constants/AnalyzerPropertyKeys.cs @@ -2,9 +2,7 @@ namespace NUnit.Analyzers.Constants { internal static class AnalyzerPropertyKeys { - internal const string HasToleranceValue = nameof(AnalyzerPropertyKeys.HasToleranceValue); internal const string ModelName = nameof(AnalyzerPropertyKeys.ModelName); internal const string MinimumNumberOfArguments = nameof(AnalyzerPropertyKeys.MinimumNumberOfArguments); - internal const string ComparerParameterIndex = nameof(AnalyzerPropertyKeys.ComparerParameterIndex); } } diff --git a/src/nunit.analyzers/Constants/NUnitFrameworkConstants.cs b/src/nunit.analyzers/Constants/NUnitFrameworkConstants.cs index 067c147b..300763e9 100644 --- a/src/nunit.analyzers/Constants/NUnitFrameworkConstants.cs +++ b/src/nunit.analyzers/Constants/NUnitFrameworkConstants.cs @@ -189,9 +189,23 @@ public static class NUnitFrameworkConstants public const string NameOfExpectedResult = "ExpectedResult"; public const string NameOfActualParameter = "actual"; + public const string NameOfADoubleParameter = "aDouble"; + public const string NameOfAnObjectParameter = "anObject"; + public const string NameOfArg1Parameter = "arg1"; + public const string NameOfArg2Parameter = "arg2"; + public const string NameOfArgsParameter = "args"; + public const string NameOfAStringParameter = "aString"; + public const string NameOfCollectionParameter = "collection"; + public const string NameOfComparerParameter = "comparer"; public const string NameOfConditionParameter = "condition"; + public const string NameOfDeltaParameter = "delta"; public const string NameOfExpectedParameter = "expected"; + public const string NameOfExpectedTypeParameter = "expectedType"; public const string NameOfExpressionParameter = "expression"; + public const string NameOfMessageParameter = "message"; + public const string NameOfPatternParameter = "pattern"; + public const string NameOfSubsetParameter = "subset"; + public const string NameOfSupersetParameter = "superset"; public const string NameOfConstraintExpressionAnd = "And"; public const string NameOfConstraintExpressionOr = "Or"; diff --git a/src/nunit.analyzers/Helpers/CodeFixHelper.cs b/src/nunit.analyzers/Helpers/CodeFixHelper.cs index f1ac40a5..0fe35241 100644 --- a/src/nunit.analyzers/Helpers/CodeFixHelper.cs +++ b/src/nunit.analyzers/Helpers/CodeFixHelper.cs @@ -35,6 +35,40 @@ internal static class CodeFixHelper return invocationExpression.ReplaceNode(invocationTargetNode, newInvocationTargetNode); } + /// + /// This is assumed to be arguments for an 'Assert.That(actual, constraint, "...: {0} - {1}", param0, param1)` + /// which needs converting into 'Assert.That(actual, constraint, $"...: {param0} - {param1}"). + /// + /// The argument that corresponds to the composite format string. + /// The list of arguments that correspond to format items. + public static ArgumentSyntax? GetInterpolatedMessageArgumentOrDefault(ArgumentSyntax? messageArgument, List args) + { + if (messageArgument is null) + return null; + + var formatSpecificationArgument = messageArgument.Expression; + if (formatSpecificationArgument.IsKind(SyntaxKind.NullLiteralExpression)) + return null; + + // We only support converting if the format specification is a constant string. + if (args.Count == 0 || formatSpecificationArgument is not LiteralExpressionSyntax literalExpression) + return messageArgument; + + var formatSpecification = literalExpression.Token.ValueText; + var formatArgumentExpressions = + (args.Count == 1 && args[0].Expression is ImplicitArrayCreationExpressionSyntax argsArrayExpression) + ? argsArrayExpression.Initializer.Expressions + : args.Select(x => x.Expression); + + var interpolatedStringContent = UpdateStringFormatToFormattableString( + formatSpecification, + formatArgumentExpressions.Select(e => e.WithoutTrivia()).ToArray()); + var interpolatedString = SyntaxFactory.InterpolatedStringExpression( + SyntaxFactory.Token(SyntaxKind.InterpolatedStringStartToken), + SyntaxFactory.List(interpolatedStringContent)); + return SyntaxFactory.Argument(interpolatedString); + } + /// /// This is assumed to be arguments for an 'Assert.That(actual, constraint, "...: {0} - {1}", param0, param1)` /// which needs converting into 'Assert.That(actual, constraint, $"...: {param0} - {param1}"). diff --git a/src/nunit.analyzers/StringAssertUsage/StringAssertUsageCodeFix.cs b/src/nunit.analyzers/StringAssertUsage/StringAssertUsageCodeFix.cs index f9da94a4..672d73dd 100644 --- a/src/nunit.analyzers/StringAssertUsage/StringAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/StringAssertUsage/StringAssertUsageCodeFix.cs @@ -16,6 +16,21 @@ namespace NUnit.Analyzers.StringAssertUsage [Shared] internal class StringAssertUsageCodeFix : ClassicModelAssertUsageCodeFix { + internal static readonly ImmutableDictionary StringAssertToExpectedParameterName = + new Dictionary() + { + { NameOfStringAssertContains, NameOfExpectedParameter }, + { NameOfStringAssertDoesNotContain, NameOfExpectedParameter }, + { NameOfStringAssertStartsWith, NameOfExpectedParameter }, + { NameOfStringAssertDoesNotStartWith, NameOfExpectedParameter }, + { NameOfStringAssertEndsWith, NameOfExpectedParameter }, + { NameOfStringAssertDoesNotEndWith, NameOfExpectedParameter }, + { NameOfStringAssertAreEqualIgnoringCase, NameOfExpectedParameter }, + { NameOfStringAssertAreNotEqualIgnoringCase, NameOfExpectedParameter }, + { NameOfStringAssertIsMatch, NameOfPatternParameter }, + { NameOfStringAssertDoesNotMatch, NameOfPatternParameter }, + }.ToImmutableDictionary(); + private static readonly ImmutableDictionary StringAssertToConstraints = new Dictionary { @@ -34,16 +49,18 @@ internal class StringAssertUsageCodeFix : ClassicModelAssertUsageCodeFix public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create( AnalyzerIdentifiers.StringAssertUsage); - protected override void UpdateArguments(Diagnostic diagnostic, List arguments) + protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArgument) ConstructActualAndConstraintArguments( + Diagnostic diagnostic, + IReadOnlyDictionary argumentNamesToArguments) { - string methodName = diagnostic.Properties[AnalyzerPropertyKeys.ModelName]!; - if (StringAssertToConstraints.TryGetValue(methodName, out Constraints? constraints)) - { - arguments.Insert(2, Argument(constraints.CreateConstraint(arguments[0]))); + var methodName = diagnostic.Properties[AnalyzerPropertyKeys.ModelName]!; + var expectedParameterName = StringAssertToExpectedParameterName[methodName]; + var expectedArgument = argumentNamesToArguments[expectedParameterName].WithNameColon(null); + var constraints = StringAssertToConstraints[methodName]; + var constraintArgument = Argument(constraints.CreateConstraint(expectedArgument)); - // Then we have to remove the 1st argument because that's now in the "constaint" - arguments.RemoveAt(0); - } + var actualArgument = argumentNamesToArguments[NameOfActualParameter].WithNameColon(null); + return (actualArgument, constraintArgument); } } } diff --git a/src/nunit.analyzers/UpdateStringFormatToInterpolatableString/UpdateStringFormatToInterpolatableStringAnalyzer.cs b/src/nunit.analyzers/UpdateStringFormatToInterpolatableString/UpdateStringFormatToInterpolatableStringAnalyzer.cs index a1f6371f..093ce1a1 100644 --- a/src/nunit.analyzers/UpdateStringFormatToInterpolatableString/UpdateStringFormatToInterpolatableStringAnalyzer.cs +++ b/src/nunit.analyzers/UpdateStringFormatToInterpolatableString/UpdateStringFormatToInterpolatableStringAnalyzer.cs @@ -107,7 +107,7 @@ private static void AnalyzeNUnit4AssertInvocation(OperationAnalysisContext conte IParameterSymbol parameter = parameters[formatParameterIndex]; if (parameter.IsOptional) { - if (parameter.Name == "message") + if (parameter.Name == NUnitFrameworkConstants.NameOfMessageParameter) break; // Overload with FormattableString or Func overload