diff --git a/src/FluentAssertions.Analyzers.Tests/Tips/CollectionTests.cs b/src/FluentAssertions.Analyzers.Tests/Tips/CollectionTests.cs index 23d206cb..c7e06cc4 100644 --- a/src/FluentAssertions.Analyzers.Tests/Tips/CollectionTests.cs +++ b/src/FluentAssertions.Analyzers.Tests/Tips/CollectionTests.cs @@ -29,7 +29,7 @@ public class CollectionTests oldAssertion: "actual.ToArray().Any().Should().BeTrue({0}).And.ToString();", newAssertion: "actual.ToArray().Should().NotBeEmpty({0}).And.ToString();")] [Implemented] - public void ExpressionBodyAssertion_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixExpressionBody(oldAssertion, newAssertion); + public void ExpressionBodyAssertion_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixExpressionBody(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Any().Should().BeTrue({0});")] @@ -53,7 +53,7 @@ public class CollectionTests oldAssertion: "actual.ToArray().Any().Should().BeTrue({0}).And.ToString();", newAssertion: "actual.ToArray().Should().NotBeEmpty({0}).And.ToString();")] [Implemented] - public void CollectionsShouldNotBeEmpty_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionsShouldNotBeEmpty_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Any().Should().BeTrue({0});")] @@ -77,7 +77,7 @@ public class CollectionTests oldAssertion: "actual.ToArray().Any().Should().BeTrue({0}).And.ToString();", newAssertion: "actual.ToArray().Should().NotBeEmpty({0}).And.ToString();")] [Implemented] - public void CollectionsShouldNotBeEmpty_Array_TestCodeFix(string oldAssertion, string newAssertion) => VerifyArrayCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionsShouldNotBeEmpty_Array_TestCodeFix(string oldAssertion, string newAssertion) => VerifyArrayCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Any().Should().BeFalse({0});")] @@ -121,7 +121,7 @@ public class CollectionTests oldAssertion: "actual.ToArray().Should().HaveCount(0{0}).And.ToString();", newAssertion: "actual.ToArray().Should().BeEmpty({0}).And.ToString();")] [Implemented] - public void CollectionShouldBeEmpty_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldBeEmpty_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Any(x => x.BooleanProperty).Should().BeTrue({0});")] @@ -165,7 +165,7 @@ public class CollectionTests oldAssertion: "actual.ToArray().Where(x => x.BooleanProperty).Should().NotBeEmpty({0}).And.ToString();", newAssertion: "actual.ToArray().Should().Contain(x => x.BooleanProperty{0}).And.ToString();")] [Implemented] - public void CollectionShouldContainProperty_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldContainProperty_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Any(x => x.BooleanProperty).Should().BeFalse({0});")] @@ -213,7 +213,7 @@ public class CollectionTests newAssertion: "actual.AsEnumerable().Should().NotContain(x => x.BooleanProperty{0}).And.ToString();", ignore: true)] [Implemented] - public void CollectionShouldNotContainProperty_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldNotContainProperty_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.All(x => x.BooleanProperty).Should().BeTrue({0});")] @@ -237,7 +237,7 @@ public class CollectionTests oldAssertion: "actual.ToArray().All(x => x.BooleanProperty).Should().BeTrue({0}).And.ToString();", newAssertion: "actual.ToArray().Should().OnlyContain(x => x.BooleanProperty{0}).And.ToString();")] [Implemented] - public void CollectionShouldOnlyContainProperty_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldOnlyContainProperty_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Contains(expectedItem).Should().BeTrue({0});")] @@ -261,7 +261,7 @@ public class CollectionTests oldAssertion: "actual.ToArray().Contains(expectedItem).Should().BeTrue({0}).And.ToString();", newAssertion: "actual.ToArray().Should().Contain(expectedItem{0}).And.ToString();")] [Implemented] - public void CollectionShouldContainItem_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldContainItem_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Contains(unexpectedItem).Should().BeFalse({0});")] @@ -285,7 +285,7 @@ public class CollectionTests oldAssertion: "actual.ToArray().Contains(unexpectedItem).Should().BeFalse({0}).And.ToString();", newAssertion: "actual.ToArray().Should().NotContain(unexpectedItem{0}).And.ToString();")] [Implemented] - public void CollectionShouldNotContainItem_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldNotContainItem_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Count().Should().Be(k{0});")] @@ -355,7 +355,7 @@ public class CollectionTests oldAssertion: "actual.ToArray().Length.Should().Be(6{0}).And.ToString();", newAssertion: "actual.ToArray().Should().HaveCount(6{0}).And.ToString();")] [Implemented] - public void CollectionShouldHaveCount_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldHaveCount_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Count().Should().BeGreaterThan(k{0});")] @@ -379,7 +379,7 @@ public class CollectionTests oldAssertion: "actual.AsEnumerable().Count().Should().BeGreaterThan(6{0}).And.ToString();", newAssertion: "actual.AsEnumerable().Should().HaveCountGreaterThan(6{0}).And.ToString();")] [Implemented] - public void CollectionShouldHaveCountGreaterThan_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldHaveCountGreaterThan_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Count().Should().BeGreaterOrEqualTo(k{0});")] @@ -403,7 +403,7 @@ public class CollectionTests oldAssertion: "actual.AsEnumerable().Count().Should().BeGreaterOrEqualTo(6{0}).And.ToString();", newAssertion: "actual.AsEnumerable().Should().HaveCountGreaterOrEqualTo(6{0}).And.ToString();")] [Implemented] - public void CollectionShouldHaveCountGreaterOrEqualTo_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldHaveCountGreaterOrEqualTo_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Count().Should().BeLessThan(k{0});")] @@ -443,7 +443,7 @@ public class CollectionTests oldAssertion: "actual.ToArray().Count().Should().BeLessThan(6{0}).And.ToString();", newAssertion: "actual.ToArray().Should().HaveCountLessThan(6{0}).And.ToString();")] [Implemented] - public void CollectionShouldHaveCountLessThan_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldHaveCountLessThan_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Count().Should().BeLessOrEqualTo(k{0});")] @@ -483,7 +483,7 @@ public class CollectionTests oldAssertion: "actual.ToArray().Count().Should().BeLessOrEqualTo(6{0}).And.ToString();", newAssertion: "actual.ToArray().Should().HaveCountLessOrEqualTo(6{0}).And.ToString();")] [Implemented] - public void CollectionShouldHaveCountLessOrEqualTo_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldHaveCountLessOrEqualTo_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Count().Should().NotBe(k{0});")] @@ -523,7 +523,7 @@ public class CollectionTests oldAssertion: "actual.ToArray().Count().Should().NotBe(6{0}).And.ToString();", newAssertion: "actual.ToArray().Should().NotHaveCount(6{0}).And.ToString();")] [Implemented] - public void CollectionShouldNotHaveCount_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldNotHaveCount_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Should().HaveCount(expected.Count(){0});")] @@ -547,7 +547,7 @@ public class CollectionTests oldAssertion: "actual.ToArray().Should().HaveCount(expected.Count(){0}).And.ToString();", newAssertion: "actual.ToArray().Should().HaveSameCount(expected{0}).And.ToString();")] [Implemented] - public void CollectionShouldHaveSameCount_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldHaveSameCount_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Count().Should().NotBe(unexpected.Count(){0});")] @@ -571,7 +571,7 @@ public class CollectionTests oldAssertion: "actual.ToArray().Count().Should().NotBe(unexpected.Count(){0}).And.ToString();", newAssertion: "actual.ToArray().Should().NotHaveSameCount(unexpected{0}).And.ToString();")] [Implemented] - public void CollectionShouldNotHaveSameCount_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldNotHaveSameCount_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Should().HaveCount(1{0});")] @@ -630,7 +630,7 @@ public void CollectionShouldContainSingle_TestAnalyzer_GenericIEnumerableShouldR oldAssertion: "actual.ToList().Should().HaveCount(1{0}).And.ToString();", newAssertion: "actual.ToList().Should().ContainSingle({0}).And.ToString();")] [Implemented] - public void CollectionShouldContainSingle_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldContainSingle_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Should().NotBeEmpty().And.NotBeNull({0});")] @@ -673,7 +673,7 @@ public void CollectionShouldContainSingle_TestAnalyzer_GenericIEnumerableShouldR oldAssertion: "actual.ToArray().Should().NotBeNull({0}).And.NotBeEmpty().And.ToString();", newAssertion: "actual.ToArray().Should().NotBeNullOrEmpty({0}).And.ToString();")] [Implemented] - public void CollectionShouldNotBeNullOrEmpty_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldNotBeNullOrEmpty_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.ElementAt(k).Should().Be(expectedItem{0});")] @@ -751,7 +751,7 @@ public void CollectionShouldContainSingle_TestAnalyzer_GenericIEnumerableShouldR oldAssertion: "actual.AsEnumerable().Skip(6).First().Should().Be(expectedItem{0}).And.ToString();", newAssertion: "actual.AsEnumerable().Should().HaveElementAt(6, expectedItem{0}).And.ToString();")] [Implemented] - public void CollectionShouldHaveElementAt_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldHaveElementAt_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.OrderBy(x => x.BooleanProperty).Should().Equal(actual{0});")] @@ -775,7 +775,7 @@ public void CollectionShouldContainSingle_TestAnalyzer_GenericIEnumerableShouldR oldAssertion: "actual.ToArray().OrderBy(x => x.BooleanProperty).Should().Equal(actual{0}).And.ToString();", newAssertion: "actual.ToArray().Should().BeInAscendingOrder(x => x.BooleanProperty{0}).And.ToString();")] [Implemented] - public void CollectionShouldBeInAscendingOrder_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldBeInAscendingOrder_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.OrderByDescending(x => x.BooleanProperty).Should().Equal(actual{0});")] @@ -799,7 +799,7 @@ public void CollectionShouldContainSingle_TestAnalyzer_GenericIEnumerableShouldR oldAssertion: "actual.ToArray().OrderByDescending(x => x.BooleanProperty).Should().Equal(actual{0}).And.ToString();", newAssertion: "actual.ToArray().Should().BeInDescendingOrder(x => x.BooleanProperty{0}).And.ToString();")] [Implemented] - public void CollectionShouldBeInDescendingOrder_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldBeInDescendingOrder_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Select(e1 => e1.BooleanProperty).Should().Equal(expected.Select(e2 => e2.BooleanProperty){0});")] @@ -823,7 +823,7 @@ public void CollectionShouldContainSingle_TestAnalyzer_GenericIEnumerableShouldR oldAssertion: "actual.ToArray().Select(e1 => e1.BooleanProperty).Should().Equal(expected.Select(e2 => e2.BooleanProperty){0}).And.ToString();", newAssertion: "actual.ToArray().Should().Equal(expected, (e1, e2) => e1.BooleanProperty == e2.BooleanProperty{0}).And.ToString();")] [Implemented] - public void CollectionShouldEqualOtherCollectionByComparer_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldEqualOtherCollectionByComparer_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Intersect(expected).Should().BeEmpty({0});")] @@ -847,7 +847,7 @@ public void CollectionShouldContainSingle_TestAnalyzer_GenericIEnumerableShouldR oldAssertion: "actual.ToArray().Intersect(expected).Should().BeEmpty({0}).And.ToString();", newAssertion: "actual.ToArray().Should().NotIntersectWith(expected{0}).And.ToString();")] [Implemented] - public void CollectionShouldNotIntersectWith_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldNotIntersectWith_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Intersect(expected).Should().NotBeEmpty({0});")] @@ -871,7 +871,7 @@ public void CollectionShouldContainSingle_TestAnalyzer_GenericIEnumerableShouldR oldAssertion: "actual.ToArray().Intersect(expected).Should().NotBeEmpty({0}).And.ToString();", newAssertion: "actual.ToArray().Should().IntersectWith(expected{0}).And.ToString();")] [Implemented] - public void CollectionShouldIntersectWith_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldIntersectWith_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Select(x => x.BooleanProperty).Should().NotContainNulls({0});")] @@ -887,7 +887,7 @@ public void CollectionShouldContainSingle_TestAnalyzer_GenericIEnumerableShouldR oldAssertion: "actual.AsEnumerable().Select(x => x.BooleanProperty).Should().NotContainNulls({0}).And.ToString();", newAssertion: "actual.AsEnumerable().Should().NotContainNulls(x => x.BooleanProperty{0}).And.ToString();")] [Ignore("What Should Happen?")] - public void CollectionShouldNotContainNulls_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldNotContainNulls_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Should().HaveSameCount(actual.Distinct(){0});")] @@ -911,7 +911,7 @@ public void CollectionShouldContainSingle_TestAnalyzer_GenericIEnumerableShouldR oldAssertion: "actual.ToArray().Should().HaveSameCount(actual.ToArray().Distinct(){0}).And.ToString();", newAssertion: "actual.ToArray().Should().OnlyHaveUniqueItems({0}).And.ToString();")] [Implemented] - public void CollectionShouldOnlyHaveUniqueItems_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldOnlyHaveUniqueItems_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Select(x => x.BooleanProperty).Should().OnlyHaveUniqueItems({0});")] @@ -935,7 +935,7 @@ public void CollectionShouldContainSingle_TestAnalyzer_GenericIEnumerableShouldR oldAssertion: "actual.ToArray().Select(x => x.BooleanProperty).Should().OnlyHaveUniqueItems({0}).And.ToString();", newAssertion: "actual.ToArray().Should().OnlyHaveUniqueItems(x => x.BooleanProperty{0}).And.ToString();")] [Implemented] - public void CollectionShouldOnlyHaveUniqueItemsByComparer_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldOnlyHaveUniqueItemsByComparer_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.FirstOrDefault().Should().BeNull({0});")] @@ -953,7 +953,7 @@ public void CollectionShouldContainSingle_TestAnalyzer_GenericIEnumerableShouldR newAssertion: "actual.AsEnumerable().Should().HaveElementAt(0, null{0}).And.ToString();")] [NotImplemented] [Ignore("What Should Happen?")] - public void CollectionShouldHaveElementAt0Null_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); + public void CollectionShouldHaveElementAt0Null_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock(oldAssertion, newAssertion); private void VerifyCSharpDiagnosticCodeBlock(string sourceAssertion, DiagnosticMetadata metadata) { diff --git a/src/FluentAssertions.Analyzers.Tests/Tips/NumericTests.cs b/src/FluentAssertions.Analyzers.Tests/Tips/NumericTests.cs index 4589d752..ff9009d1 100644 --- a/src/FluentAssertions.Analyzers.Tests/Tips/NumericTests.cs +++ b/src/FluentAssertions.Analyzers.Tests/Tips/NumericTests.cs @@ -11,7 +11,7 @@ public class NumericTests [AssertionDiagnostic("actual.Should().BeGreaterThan(0{0});")] [AssertionDiagnostic("actual.Should().BeGreaterThan(0{0}).ToString();")] [Implemented] - public void NumericShouldBePositive_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion); + public void NumericShouldBePositive_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion, DiagnosticMetadata.NumericShouldBePositive_ShouldBeGreaterThan); [DataTestMethod] [AssertionCodeFix( @@ -21,13 +21,13 @@ public class NumericTests oldAssertion: "actual.Should().BeGreaterThan(0{0}).ToString();", newAssertion: "actual.Should().BePositive({0}).ToString();")] [Implemented] - public void NumericShouldBePositive_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix(oldAssertion, newAssertion); + public void NumericShouldBePositive_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Should().BeLessThan(0{0});")] [AssertionDiagnostic("actual.Should().BeLessThan(0{0}).ToString();")] [Implemented] - public void NumericShouldBeNegative_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion); + public void NumericShouldBeNegative_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion, DiagnosticMetadata.NumericShouldBeNegative_ShouldBeLessThan); [DataTestMethod] [AssertionCodeFix( @@ -37,17 +37,19 @@ public class NumericTests oldAssertion: "actual.Should().BeLessThan(0{0}).ToString();", newAssertion: "actual.Should().BeNegative({0}).ToString();")] [Implemented] - public void NumericShouldBeNegative_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix(oldAssertion, newAssertion); + public void NumericShouldBeNegative_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("actual.Should().BeGreaterOrEqualTo(lower{0}).And.BeLessOrEqualTo(upper);")] [AssertionDiagnostic("actual.Should().BeGreaterOrEqualTo(lower).And.BeLessOrEqualTo(upper{0});")] - [AssertionDiagnostic("actual.Should().BeGreaterOrEqualTo(lower{0}).And.BeLessOrEqualTo(upper{0});")] + [Implemented] + public void NumericShouldBeInRange_BeGreaterOrEqualToAndBeLessOrEqualTo_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion, DiagnosticMetadata.NumericShouldBeInRange_BeGreaterOrEqualToAndBeLessOrEqualTo); + + [DataTestMethod] [AssertionDiagnostic("actual.Should().BeLessOrEqualTo(upper{0}).And.BeGreaterOrEqualTo(lower);")] [AssertionDiagnostic("actual.Should().BeLessOrEqualTo(upper).And.BeGreaterOrEqualTo(lower{0});")] - [AssertionDiagnostic("actual.Should().BeLessOrEqualTo(upper{0}).And.BeGreaterOrEqualTo(lower{0});")] [Implemented] - public void NumericShouldBeInRange_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion); + public void NumericShouldBeInRange_BeLessOrEqualToAndBeGreaterOrEqualTo_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion, DiagnosticMetadata.NumericShouldBeInRange_BeLessOrEqualToAndBeGreaterOrEqualTo); [DataTestMethod] [AssertionCodeFix( @@ -62,33 +64,30 @@ public class NumericTests [AssertionCodeFix( oldAssertion: "actual.Should().BeLessOrEqualTo(upper).And.BeGreaterOrEqualTo(lower{0});", newAssertion: "actual.Should().BeInRange(lower, upper{0});")] - [Implemented] - public void NumericShouldBeInRange_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix(oldAssertion, newAssertion); + [NotImplemented] + public void NumericShouldBeInRange_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix(oldAssertion, newAssertion); [DataTestMethod] [AssertionDiagnostic("Math.Abs(expected - actual).Should().BeLessOrEqualTo(delta{0});")] [Implemented] - public void NumericShouldBeApproximately_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion); + public void NumericShouldBeApproximately_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion, DiagnosticMetadata.NumericShouldBeApproximately_MathAbsShouldBeLessOrEqualTo); [DataTestMethod] [AssertionCodeFix( oldAssertion: "Math.Abs(expected - actual).Should().BeLessOrEqualTo(delta{0});", newAssertion: "actual.Should().BeApproximately(expected, delta{0});")] [Implemented] - public void NumericShouldBeApproximately_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix(oldAssertion, newAssertion); + public void NumericShouldBeApproximately_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix(oldAssertion, newAssertion); - private void VerifyCSharpDiagnostic(string sourceAssertion) where TDiagnosticAnalyzer : Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer, new() + private void VerifyCSharpDiagnostic(string sourceAssertion, DiagnosticMetadata metadata) { var source = GenerateCode.DoubleAssertion(sourceAssertion); - var type = typeof(TDiagnosticAnalyzer); - var diagnosticId = (string)type.GetField("DiagnosticId").GetValue(null); - var message = (string)type.GetField("Message").GetValue(null); - DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(source, new DiagnosticResult { - Id = diagnosticId, - Message = message, + Id = FluentAssertionsOperationAnalyzer.DiagnosticId, + Message = metadata.Message, + VisitorName = metadata.Name, Locations = new DiagnosticResultLocation[] { new DiagnosticResultLocation("Test0.cs", 10, 13) diff --git a/src/FluentAssertions.Analyzers/Tips/DiagnosticMetadata.cs b/src/FluentAssertions.Analyzers/Tips/DiagnosticMetadata.cs index b4887ad0..b37f5079 100644 --- a/src/FluentAssertions.Analyzers/Tips/DiagnosticMetadata.cs +++ b/src/FluentAssertions.Analyzers/Tips/DiagnosticMetadata.cs @@ -54,5 +54,11 @@ private DiagnosticMetadata(string message, string helpLink, [CallerMemberName] s public static DiagnosticMetadata CollectionShouldOnlyHaveUniqueItems_ShouldHaveSameCountThisCollectionDistinct { get; } = new("Use .Should().OnlyHaveUniqueItems()", GetHelpLink("Collections-33")); public static DiagnosticMetadata CollectionShouldOnlyHaveUniqueItemsByComparer_SelectShouldOnlyHaveUniqueItems { get; } = new("Use .Should().OnlyHaveUniqueItems()", GetHelpLink("Collections-34")); + public static DiagnosticMetadata NumericShouldBePositive_ShouldBeGreaterThan { get; } = new("Use .Should().BePositive()", GetHelpLink("Numeric-1")); + public static DiagnosticMetadata NumericShouldBeNegative_ShouldBeLessThan { get; } = new("Use .Should().BeNegative()", GetHelpLink("Numeric-2")); + public static DiagnosticMetadata NumericShouldBeInRange_BeGreaterOrEqualToAndBeLessOrEqualTo { get; } = new("Use .Should().BeInRange()", string.Empty); + public static DiagnosticMetadata NumericShouldBeInRange_BeLessOrEqualToAndBeGreaterOrEqualTo { get; } = new("Use .Should().BeInRange()", string.Empty); + public static DiagnosticMetadata NumericShouldBeApproximately_MathAbsShouldBeLessOrEqualTo { get; } = new("Use .Should().BeApproximately()", string.Empty); + private static string GetHelpLink(string id) => $"https://fluentassertions.com/tips/#{id}"; } \ No newline at end of file diff --git a/src/FluentAssertions.Analyzers/Tips/CollectionCodeFix.CollectionShouldBeEmpty.cs b/src/FluentAssertions.Analyzers/Tips/FluentAssertionsCodeFix.CollectionShouldBeEmpty.cs similarity index 100% rename from src/FluentAssertions.Analyzers/Tips/CollectionCodeFix.CollectionShouldBeEmpty.cs rename to src/FluentAssertions.Analyzers/Tips/FluentAssertionsCodeFix.CollectionShouldBeEmpty.cs diff --git a/src/FluentAssertions.Analyzers/Tips/CollectionCodeFix.CollectionShouldEqualOtherCollectionByComparer.cs b/src/FluentAssertions.Analyzers/Tips/FluentAssertionsCodeFix.CollectionShouldEqualOtherCollectionByComparer.cs similarity index 97% rename from src/FluentAssertions.Analyzers/Tips/CollectionCodeFix.CollectionShouldEqualOtherCollectionByComparer.cs rename to src/FluentAssertions.Analyzers/Tips/FluentAssertionsCodeFix.CollectionShouldEqualOtherCollectionByComparer.cs index 9af3683d..17fc0ab6 100644 --- a/src/FluentAssertions.Analyzers/Tips/CollectionCodeFix.CollectionShouldEqualOtherCollectionByComparer.cs +++ b/src/FluentAssertions.Analyzers/Tips/FluentAssertionsCodeFix.CollectionShouldEqualOtherCollectionByComparer.cs @@ -4,7 +4,7 @@ namespace FluentAssertions.Analyzers; -public partial class CollectionCodeFix +public partial class FluentAssertionsCodeFix { private ExpressionSyntax GetNewExpressionForSelectShouldEqualOtherCollectionSelectSyntaxVisitor(ExpressionSyntax expression) { diff --git a/src/FluentAssertions.Analyzers/Tips/CollectionCodeFix.CollectionShouldNotBeNullOrEmpty.cs b/src/FluentAssertions.Analyzers/Tips/FluentAssertionsCodeFix.CollectionShouldNotBeNullOrEmpty.cs similarity index 57% rename from src/FluentAssertions.Analyzers/Tips/CollectionCodeFix.CollectionShouldNotBeNullOrEmpty.cs rename to src/FluentAssertions.Analyzers/Tips/FluentAssertionsCodeFix.CollectionShouldNotBeNullOrEmpty.cs index ff5627e9..422168e7 100644 --- a/src/FluentAssertions.Analyzers/Tips/CollectionCodeFix.CollectionShouldNotBeNullOrEmpty.cs +++ b/src/FluentAssertions.Analyzers/Tips/FluentAssertionsCodeFix.CollectionShouldNotBeNullOrEmpty.cs @@ -2,13 +2,16 @@ namespace FluentAssertions.Analyzers; -public partial class CollectionCodeFix +public partial class FluentAssertionsCodeFix { private ExpressionSyntax GetCombinedAssertions(ExpressionSyntax expression, string removeMethod, string renameMethod) + => GetCombinedAssertions(expression, removeMethod, renameMethod, "NotBeNullOrEmpty"); + + private ExpressionSyntax GetCombinedAssertions(ExpressionSyntax expression, string removeMethod, string renameMethod, string newMethod) { var remove = NodeReplacement.RemoveAndExtractArguments(removeMethod); var newExpression = GetNewExpression(expression, NodeReplacement.RemoveMethodBefore(removeMethod), remove); - return GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments(renameMethod, "NotBeNullOrEmpty", remove.Arguments)); + return GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments(renameMethod, newMethod, remove.Arguments)); } } \ No newline at end of file diff --git a/src/FluentAssertions.Analyzers/Tips/CollectionCodeFix.cs b/src/FluentAssertions.Analyzers/Tips/FluentAssertionsCodeFix.cs similarity index 80% rename from src/FluentAssertions.Analyzers/Tips/CollectionCodeFix.cs rename to src/FluentAssertions.Analyzers/Tips/FluentAssertionsCodeFix.cs index 034f86a2..e3fc6b41 100644 --- a/src/FluentAssertions.Analyzers/Tips/CollectionCodeFix.cs +++ b/src/FluentAssertions.Analyzers/Tips/FluentAssertionsCodeFix.cs @@ -2,12 +2,13 @@ using System.Composition; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace FluentAssertions.Analyzers; -[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CollectionCodeFix)), Shared] -public partial class CollectionCodeFix : FluentAssertionsCodeFixProvider +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(FluentAssertionsCodeFix)), Shared] +public partial class FluentAssertionsCodeFix : FluentAssertionsCodeFixProvider { public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(FluentAssertionsOperationAnalyzer.DiagnosticId); @@ -188,6 +189,54 @@ protected override ExpressionSyntax GetNewExpression(ExpressionSyntax expression return GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments("Be", "HaveElementAt", remove.Arguments)); } + case nameof(DiagnosticMetadata.NumericShouldBePositive_ShouldBeGreaterThan): + return GetNewExpression(expression, NodeReplacement.RenameAndRemoveFirstArgument("BeGreaterThan", "BePositive")); + case nameof(DiagnosticMetadata.NumericShouldBeNegative_ShouldBeLessThan): + return GetNewExpression(expression, NodeReplacement.RenameAndRemoveFirstArgument("BeLessThan", "BeNegative")); + + case nameof(DiagnosticMetadata.NumericShouldBeInRange_BeGreaterOrEqualToAndBeLessOrEqualTo): + { + var removeLess = NodeReplacement.RemoveAndExtractArguments("BeLessOrEqualTo"); + var newExpression = GetNewExpression(expression, NodeReplacement.RemoveMethodBefore("BeLessOrEqualTo"), removeLess); + + var renameGreater = NodeReplacement.RenameAndExtractArguments("BeGreaterOrEqualTo", "BeInRange"); + newExpression = GetNewExpression(newExpression, renameGreater); + + var arguments = renameGreater.Arguments.InsertRange(1, removeLess.Arguments); + + var result = GetNewExpression(newExpression, NodeReplacement.WithArguments("BeInRange", arguments)); + + return result; + } + case nameof(DiagnosticMetadata.NumericShouldBeInRange_BeLessOrEqualToAndBeGreaterOrEqualTo): + { + var removeGreater = NodeReplacement.RemoveAndExtractArguments("BeGreaterOrEqualTo"); + var newExpression = GetNewExpression(expression, NodeReplacement.RemoveMethodBefore("BeGreaterOrEqualTo"), removeGreater); + + var renameLess = NodeReplacement.RenameAndExtractArguments("BeLessOrEqualTo", "BeInRange"); + newExpression = GetNewExpression(newExpression, renameLess); + + var arguments = removeGreater.Arguments.InsertRange(1, renameLess.Arguments); + + return GetNewExpression(newExpression, NodeReplacement.WithArguments("BeInRange", arguments)); + } + case nameof(DiagnosticMetadata.NumericShouldBeApproximately_MathAbsShouldBeLessOrEqualTo): + { + var remove = NodeReplacement.RemoveAndExtractArguments("Abs"); + var newExpression = GetNewExpression(expression, remove); + + var subtractExpression = (BinaryExpressionSyntax)remove.Arguments[0].Expression; + + var actual = subtractExpression.Right as IdentifierNameSyntax; + var expected = subtractExpression.Left; + + newExpression = GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments("BeLessOrEqualTo", "BeApproximately", new SeparatedSyntaxList().Add(SyntaxFactory.Argument(expected)))); + + newExpression = RenameIdentifier(newExpression, "Math", actual.Identifier.Text); + + return newExpression; + } + default: throw new System.InvalidOperationException($"Invalid visitor name - {properties.VisitorName}"); }; } diff --git a/src/FluentAssertions.Analyzers/Tips/FluentAssertionsOperationAnalyzer.Utils.cs b/src/FluentAssertions.Analyzers/Tips/FluentAssertionsOperationAnalyzer.Utils.cs index 73373255..27a6a749 100644 --- a/src/FluentAssertions.Analyzers/Tips/FluentAssertionsOperationAnalyzer.Utils.cs +++ b/src/FluentAssertions.Analyzers/Tips/FluentAssertionsOperationAnalyzer.Utils.cs @@ -1,3 +1,4 @@ +using System; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -28,7 +29,7 @@ public FluentAssertionsMetadata(Compilation compilation) IReadonlyDictionaryOfT2 = compilation.GetTypeByMetadataName(typeof(IReadOnlyDictionary<,>).FullName); Enumerable = compilation.GetTypeByMetadataName(typeof(Enumerable).FullName); - + Math = compilation.GetTypeByMetadataName(typeof(Math).FullName); } public INamedTypeSymbol AssertionExtensions { get; } public INamedTypeSymbol ReferenceTypeAssertionsOfT2 { get; } @@ -39,5 +40,6 @@ public FluentAssertionsMetadata(Compilation compilation) public INamedTypeSymbol BooleanAssertionsOfT1 { get; } public INamedTypeSymbol NumericAssertionsOfT2 { get; } public INamedTypeSymbol Enumerable { get; } + public INamedTypeSymbol Math { get; } } } \ No newline at end of file diff --git a/src/FluentAssertions.Analyzers/Tips/FluentAssertionsOperationAnalyzer.cs b/src/FluentAssertions.Analyzers/Tips/FluentAssertionsOperationAnalyzer.cs index c413dfcc..f50d7d0a 100644 --- a/src/FluentAssertions.Analyzers/Tips/FluentAssertionsOperationAnalyzer.cs +++ b/src/FluentAssertions.Analyzers/Tips/FluentAssertionsOperationAnalyzer.cs @@ -70,13 +70,13 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs { case nameof(Enumerable.Where) when IsEnumerableMethodWithPredicate(invocationBeforeShould, metadata): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldContainProperty_WhereShouldNotBeEmpty)); - break; + return; case nameof(Enumerable.Intersect) when invocationBeforeShould.Arguments.Length is 2: context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldIntersectWith_IntersectShouldNotBeEmpty)); - break; + return; } } - break; + return; } case "BeEmpty" when assertion.IsContainedInType(metadata.GenericCollectionAssertionsOfT3): { @@ -86,13 +86,13 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs { case nameof(Enumerable.Intersect) when invocationBeforeShould.Arguments.Length is 2: context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldNotIntersectWith_IntersectShouldBeEmpty)); - break; + return; case nameof(Enumerable.Where) when IsEnumerableMethodWithPredicate(invocationBeforeShould, metadata): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldNotContainProperty_WhereShouldBeEmpty)); - break; + return; } } - break; + return; } case "NotBeNull" when assertion.IsContainedInType(metadata.ReferenceTypeAssertionsOfT2): { @@ -104,7 +104,7 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs context.ReportDiagnostic(CreateDiagnostic(chainedInvocation, DiagnosticMetadata.CollectionShouldNotBeNullOrEmpty_ShouldNotBeNullAndNotBeEmpty)); } } - break; + return; case "NotContainNulls" when assertion.IsContainedInType(metadata.GenericCollectionAssertionsOfT3): { if (!invocation.TryGetFirstDescendent(out var invocationBeforeShould)) return; @@ -112,10 +112,10 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs { case nameof(Enumerable.Select) when IsEnumerableMethodWithPredicate(invocationBeforeShould, metadata): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldNotContainNulls_SelectShouldNotContainNulls)); - break; + return; } } - break; + return; case "Equal" when assertion.IsContainedInType(metadata.GenericCollectionAssertionsOfT3): { if (!invocation.TryGetFirstDescendent(out var invocationBeforeShould)) return; @@ -123,16 +123,16 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs { case nameof(Enumerable.OrderBy) when IsEnumerableMethodWithPredicate(invocationBeforeShould, metadata) && invocationBeforeShould.Arguments[0].IsSameArgumentReference(assertion.Arguments[0]): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldBeInAscendingOrder_OrderByShouldEqual)); - break; + return; case nameof(Enumerable.OrderByDescending) when IsEnumerableMethodWithPredicate(invocationBeforeShould, metadata) && invocationBeforeShould.Arguments[0].IsSameArgumentReference(assertion.Arguments[0]): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldBeInDescendingOrder_OrderByDescendingShouldEqual)); - break; + return; case nameof(Enumerable.Select) when IsEnumerableMethodWithPredicate(invocationBeforeShould, metadata): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldEqualOtherCollectionByComparer_SelectShouldEqualOtherCollectionSelect)); - break; + return; } } - break; + return; case "BeTrue" when assertion.IsContainedInType(metadata.BooleanAssertionsOfT1): { if (!invocation.TryGetFirstDescendent(out var invocationBeforeShould)) return; @@ -140,20 +140,20 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs { case nameof(Enumerable.Any) when IsEnumerableMethodWithoutArguments(invocationBeforeShould, metadata): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldNotBeEmpty_AnyShouldBeTrue)); - break; + return; case nameof(Enumerable.Any) when IsEnumerableMethodWithPredicate(invocationBeforeShould, metadata): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldContainProperty_AnyWithLambdaShouldBeTrue)); - break; + return; case nameof(Enumerable.All) when IsEnumerableMethodWithPredicate(invocationBeforeShould, metadata): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldOnlyContainProperty_AllShouldBeTrue)); - break; + return; case nameof(Enumerable.Contains) when invocationBeforeShould.IsContainedInType(metadata.Enumerable) && invocationBeforeShould.Arguments.Length is 2: case nameof(ICollection.Contains) when invocationBeforeShould.ImplementsOrIsInterface(SpecialType.System_Collections_Generic_ICollection_T) && invocationBeforeShould.Arguments.Length is 1: context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldContainItem_ContainsShouldBeTrue)); - break; + return; } } - break; + return; case "BeFalse" when assertion.IsContainedInType(metadata.BooleanAssertionsOfT1): { if (invocation.TryGetFirstDescendent(out var invocationBeforeShould)) @@ -162,18 +162,18 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs { case nameof(Enumerable.Any) when IsEnumerableMethodWithoutArguments(invocationBeforeShould, metadata): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldBeEmpty_AnyShouldBeFalse)); - break; + return; case nameof(Enumerable.Any) when IsEnumerableMethodWithPredicate(invocationBeforeShould, metadata): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldNotContainProperty_AnyLambdaShouldBeFalse)); - break; + return; case nameof(Enumerable.Contains) when invocationBeforeShould.IsContainedInType(metadata.Enumerable) && invocationBeforeShould.Arguments.Length is 2: case nameof(ICollection.Contains) when invocationBeforeShould.ImplementsOrIsInterface(SpecialType.System_Collections_Generic_ICollection_T) && invocationBeforeShould.Arguments.Length == 1: context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldNotContainItem_ContainsShouldBeFalse)); - break; + return; } } } - break; + return; case "HaveCount" when assertion.IsContainedInType(metadata.GenericCollectionAssertionsOfT3) && assertion.Arguments[0].IsLiteralValue(1): { if (invocation.TryGetFirstDescendent(out var invocationBeforeShould)) @@ -188,16 +188,16 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldContainSingle_ShouldHaveCount1)); } - break; + return; case "HaveCount" when assertion.IsContainedInType(metadata.GenericCollectionAssertionsOfT3) && assertion.Arguments[0].IsLiteralValue(0): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldBeEmpty_ShouldHaveCount0)); - break; + return; case "HaveCount" when assertion.IsContainedInType(metadata.GenericCollectionAssertionsOfT3) && assertion.Arguments[0].HasFirstDescendentInvocation(nameof(Enumerable.Count)): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldHaveSameCount_ShouldHaveCountOtherCollectionCount)); - break; + return; case "HaveSameCount" when assertion.IsContainedInType(metadata.GenericCollectionAssertionsOfT3) && assertion.Arguments[0].HasFirstDescendentInvocation(nameof(Enumerable.Distinct)): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldOnlyHaveUniqueItems_ShouldHaveSameCountThisCollectionDistinct)); - break; + return; case "OnlyHaveUniqueItems" when assertion.IsContainedInType(metadata.GenericCollectionAssertionsOfT3): { if (!invocation.TryGetFirstDescendent(out var invocationBeforeShould)) return; @@ -205,10 +205,10 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs { case nameof(Enumerable.Select) when IsEnumerableMethodWithPredicate(invocationBeforeShould, metadata): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldOnlyHaveUniqueItemsByComparer_SelectShouldOnlyHaveUniqueItems)); - break; + return; } } - break; + return; case "Be" when assertion.IsContainedInType(metadata.NumericAssertionsOfT2): { if (invocation.TryGetFirstDescendent(out var invocationBeforeShould)) @@ -229,7 +229,7 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs { context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldHaveCount_CountShouldBe)); } - break; + return; } } @@ -239,7 +239,7 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs { case nameof(Array.Length) when propertyBeforeShould.IsContainedInType(SpecialType.System_Array): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldHaveCount_LengthShouldBe)); - break; + return; } } if (subject is IPropertyReferenceOperation propertyReference && propertyReference.Property.Name == WellKnownMemberNames.Indexer @@ -247,7 +247,7 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs { context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldHaveElementAt_IndexerShouldBe)); } - break; + return; } case "Be" when assertion.IsContainedInType(metadata.ObjectAssertionsOfT2): { @@ -258,7 +258,7 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs // TODO: add support when element type is Numeric case nameof(Enumerable.ElementAt) when invocationBeforeShould.IsContainedInType(metadata.Enumerable) && invocationBeforeShould.Arguments.Length is 2: context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldHaveElementAt_ElementAtIndexShouldBe)); - break; + return; case nameof(Enumerable.First) when IsEnumerableMethodWithoutArguments(invocationBeforeShould, metadata): { if (invocationBeforeShould.TryGetFirstDescendent(out var skipInvocation) && skipInvocation.TargetMethod.Name is nameof(Enumerable.Skip)) @@ -266,7 +266,7 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldHaveElementAt_SkipFirstShouldBe)); } } - break; + return; } } if (subject.TryGetFirstDescendent(out var arrayElementReference)) @@ -275,12 +275,12 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs } else if (subject.TryGetFirstDescendent(out var propertyReference) && propertyReference.Property.IsIndexer) { - if (propertyReference.Instance.Type.ImplementsOrIsInterface(metadata.IDictionaryOfT2) || propertyReference.Instance.Type.ImplementsOrIsInterface(metadata.IReadonlyDictionaryOfT2)) break; + if (propertyReference.Instance.Type.ImplementsOrIsInterface(metadata.IDictionaryOfT2) || propertyReference.Instance.Type.ImplementsOrIsInterface(metadata.IReadonlyDictionaryOfT2)) return; context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldHaveElementAt_IndexerShouldBe)); } } - break; + return; case "NotBe" when assertion.IsContainedInType(metadata.NumericAssertionsOfT2): { if (invocation.TryGetFirstDescendent(out var invocationBeforeShould)) @@ -299,11 +299,11 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldNotHaveCount_CountShouldNotBe)); } - break; + return; } } } - break; + return; case "BeGreaterOrEqualTo" when assertion.IsContainedInType(metadata.NumericAssertionsOfT2): { if (invocation.TryGetFirstDescendent(out var invocationBeforeShould)) @@ -313,11 +313,19 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs // TODO: add support for Enumerable.LongCount case nameof(Enumerable.Count) when IsEnumerableMethodWithoutArguments(invocationBeforeShould, metadata): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldHaveCountGreaterOrEqualTo_CountShouldBeGreaterOrEqualTo)); - break; + return; } } + + if (assertion.TryGetChainedInvocationAfterAndConstraint("BeLessOrEqualTo", out var chainedInvocation)) + { + if (!assertion.HasEmptyBecauseAndReasonArgs(startingIndex: 1) && !chainedInvocation.HasEmptyBecauseAndReasonArgs(startingIndex: 1)) return; + + context.ReportDiagnostic(CreateDiagnostic(chainedInvocation, DiagnosticMetadata.NumericShouldBeInRange_BeGreaterOrEqualToAndBeLessOrEqualTo)); + return; + } } - break; + return; case "BeGreaterThan" when assertion.IsContainedInType(metadata.NumericAssertionsOfT2): { if (invocation.TryGetFirstDescendent(out var invocationBeforeShould)) @@ -327,11 +335,16 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs // TODO: add support for Enumerable.LongCount case nameof(Enumerable.Count) when IsEnumerableMethodWithoutArguments(invocationBeforeShould, metadata): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldHaveCountGreaterThan_CountShouldBeGreaterThan)); - break; + return; } } + if (assertion.Arguments[0].IsLiteralValue(0)) + { + context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.NumericShouldBePositive_ShouldBeGreaterThan)); + return; + } } - break; + return; case "BeLessOrEqualTo" when assertion.IsContainedInType(metadata.NumericAssertionsOfT2): { if (invocation.TryGetFirstDescendent(out var invocationBeforeShould)) @@ -341,11 +354,22 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs // TODO: add support for Enumerable.LongCount case nameof(Enumerable.Count) when IsEnumerableMethodWithoutArguments(invocationBeforeShould, metadata): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldHaveCountLessOrEqualTo_CountShouldBeLessOrEqualTo)); - break; + return; + case nameof(Math.Abs) when invocationBeforeShould.IsContainedInType(metadata.Math) && assertion.Arguments[0].IsReferenceOfType(): + context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.NumericShouldBeApproximately_MathAbsShouldBeLessOrEqualTo)); + return; } } + + if (assertion.TryGetChainedInvocationAfterAndConstraint("BeGreaterOrEqualTo", out var chainedInvocation)) + { + if (!assertion.HasEmptyBecauseAndReasonArgs(startingIndex: 1) && !chainedInvocation.HasEmptyBecauseAndReasonArgs(startingIndex: 1)) return; + + context.ReportDiagnostic(CreateDiagnostic(chainedInvocation, DiagnosticMetadata.NumericShouldBeInRange_BeLessOrEqualToAndBeGreaterOrEqualTo)); + return; + } } - break; + return; case "BeLessThan" when assertion.IsContainedInType(metadata.NumericAssertionsOfT2): { if (invocation.TryGetFirstDescendent(out var invocationBeforeShould)) @@ -355,11 +379,17 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs // TODO: add support for Enumerable.LongCount case nameof(Enumerable.Count) when IsEnumerableMethodWithoutArguments(invocationBeforeShould, metadata): context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldHaveCountLessThan_CountShouldBeLessThan)); - break; + return; } } + + if (assertion.Arguments[0].IsLiteralValue(0)) + { + context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.NumericShouldBeNegative_ShouldBeLessThan)); + return; + } } - break; + return; } } diff --git a/src/FluentAssertions.Analyzers/Tips/Numerics/NumericAnalyzer.cs b/src/FluentAssertions.Analyzers/Tips/Numerics/NumericAnalyzer.cs deleted file mode 100644 index 3e01f8c3..00000000 --- a/src/FluentAssertions.Analyzers/Tips/Numerics/NumericAnalyzer.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Microsoft.CodeAnalysis; - -namespace FluentAssertions.Analyzers; - -public abstract class NumericAnalyzer : FluentAssertionsAnalyzer -{ - protected override bool ShouldAnalyzeVariableNamedType(INamedTypeSymbol type, SemanticModel semanticModel) => true; -} diff --git a/src/FluentAssertions.Analyzers/Tips/Numerics/NumericShouldBeApproximately.cs b/src/FluentAssertions.Analyzers/Tips/Numerics/NumericShouldBeApproximately.cs deleted file mode 100644 index 074416c3..00000000 --- a/src/FluentAssertions.Analyzers/Tips/Numerics/NumericShouldBeApproximately.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Composition; -using System.Linq; - -namespace FluentAssertions.Analyzers; - -[DiagnosticAnalyzer(LanguageNames.CSharp)] -public class NumericShouldBeApproximatelyAnalyzer : NumericAnalyzer -{ - public const string DiagnosticId = Constants.Tips.Numeric.NumericShouldBeApproximately; - public const string Category = Constants.Tips.Category; - - public const string Message = "Use .Should() followed by .BeApproximately() instead."; - - protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true); - protected override IEnumerable Visitors - { - get - { - yield return new MathAbsShouldBeLessOrEqualToSyntaxVisitor(); - } - } - - private static readonly string[] ValidaTypeNames = { "double", "decimal", "float" }; - protected override bool ShouldAnalyzeVariableNamedType(INamedTypeSymbol type, SemanticModel semanticModel) - => ValidaTypeNames.Contains(type.ToDisplayString(), StringComparer.OrdinalIgnoreCase); - - public class MathAbsShouldBeLessOrEqualToSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor - { - public MathAbsShouldBeLessOrEqualToSyntaxVisitor() : base(new MemberValidator("Abs", IsSubtractExpression), MemberValidator.Should, new MemberValidator("BeLessOrEqualTo")) - { - } - - private static bool IsSubtractExpression(SeparatedSyntaxList arguments, SemanticModel semanticModel) - { - if (arguments.Count != 1) return false; - - return arguments[0].Expression is BinaryExpressionSyntax subtractExpression - && subtractExpression.IsKind(SyntaxKind.SubtractExpression) - && subtractExpression.Right.IsKind(SyntaxKind.IdentifierName); - } - } -} - -[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(NumericShouldBeApproximatelyCodeFix)), Shared] -public class NumericShouldBeApproximatelyCodeFix : FluentAssertionsCodeFixProvider -{ - public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(NumericShouldBeApproximatelyAnalyzer.DiagnosticId); - - protected override ExpressionSyntax GetNewExpression(ExpressionSyntax expression, FluentAssertionsDiagnosticProperties properties) - { - var remove = NodeReplacement.RemoveAndExtractArguments("Abs"); - var newExpression = GetNewExpression(expression, remove); - - var subtractExpression = (BinaryExpressionSyntax)remove.Arguments[0].Expression; - - var actual = subtractExpression.Right as IdentifierNameSyntax; - var expected = subtractExpression.Left; - - newExpression = GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments("BeLessOrEqualTo", "BeApproximately", new SeparatedSyntaxList().Add(SyntaxFactory.Argument(expected)))); - - newExpression = RenameIdentifier(newExpression, "Math", actual.Identifier.Text); - - return newExpression; - } -} \ No newline at end of file diff --git a/src/FluentAssertions.Analyzers/Tips/Numerics/NumericShouldBeInRange.cs b/src/FluentAssertions.Analyzers/Tips/Numerics/NumericShouldBeInRange.cs deleted file mode 100644 index 31f9eccb..00000000 --- a/src/FluentAssertions.Analyzers/Tips/Numerics/NumericShouldBeInRange.cs +++ /dev/null @@ -1,92 +0,0 @@ - -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Composition; -using System.Threading.Tasks; - -namespace FluentAssertions.Analyzers; - -[DiagnosticAnalyzer(LanguageNames.CSharp)] -public class NumericShouldBeInRangeAnalyzer : NumericAnalyzer -{ - public const string DiagnosticId = Constants.Tips.Numeric.NumericShouldBeInRange; - public const string Category = Constants.Tips.Category; - - public const string Message = "Use .Should() followed by .BeInRange() instead."; - - protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true); - protected override IEnumerable Visitors - { - get - { - yield return new BeGreaterOrEqualToAndBeLessOrEqualToSyntaxVisitor(); - yield return new BeLessOrEqualToAndBeGreaterOrEqualToSyntaxVisitor(); - } - } - - public class BeGreaterOrEqualToAndBeLessOrEqualToSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor - { - public BeGreaterOrEqualToAndBeLessOrEqualToSyntaxVisitor() : base(MemberValidator.Should, new MemberValidator("BeGreaterOrEqualTo"), MemberValidator.And, new MemberValidator("BeLessOrEqualTo")) - { - } - } - public class BeLessOrEqualToAndBeGreaterOrEqualToSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor - { - public BeLessOrEqualToAndBeGreaterOrEqualToSyntaxVisitor() : base(MemberValidator.Should, new MemberValidator("BeLessOrEqualTo"), MemberValidator.And, new MemberValidator("BeGreaterOrEqualTo")) - { - } - } -} - -[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(NumericShouldBeInRangeCodeFix)), Shared] -public class NumericShouldBeInRangeCodeFix : FluentAssertionsCodeFixProvider -{ - public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(NumericShouldBeInRangeAnalyzer.DiagnosticId); - - protected override Task CanRewriteAssertion(ExpressionSyntax expression, CodeFixContext context) - { - var visitor = new MemberAccessExpressionsCSharpSyntaxVisitor(); - expression.Accept(visitor); - - var beLessOrEqualTo = visitor.Members.Find(member => member.Name.Identifier.Text == "BeLessOrEqualTo"); - var beGreaterOrEqualTo = visitor.Members.Find(member => member.Name.Identifier.Text == "BeGreaterOrEqualTo"); - - return Task.FromResult( - !(beLessOrEqualTo.Parent is InvocationExpressionSyntax beLessOrEqualToInvocation && beLessOrEqualToInvocation.ArgumentList.Arguments.Count > 1 - && beGreaterOrEqualTo.Parent is InvocationExpressionSyntax beGreaterOrEqualToInvocation && beGreaterOrEqualToInvocation.ArgumentList.Arguments.Count > 1) - ); - } - - protected override ExpressionSyntax GetNewExpression(ExpressionSyntax expression, FluentAssertionsDiagnosticProperties properties) - { - if (properties.VisitorName == nameof(NumericShouldBeInRangeAnalyzer.BeGreaterOrEqualToAndBeLessOrEqualToSyntaxVisitor)) - { - var removeLess = NodeReplacement.RemoveAndExtractArguments("BeLessOrEqualTo"); - var newExpression = GetNewExpression(expression, NodeReplacement.RemoveMethodBefore("BeLessOrEqualTo"), removeLess); - - var renameGreater = NodeReplacement.RenameAndExtractArguments("BeGreaterOrEqualTo", "BeInRange"); - newExpression = GetNewExpression(newExpression, renameGreater); - - var arguments = renameGreater.Arguments.InsertRange(1, removeLess.Arguments); - - return GetNewExpression(newExpression, NodeReplacement.WithArguments("BeInRange", arguments)); - } - else if (properties.VisitorName == nameof(NumericShouldBeInRangeAnalyzer.BeLessOrEqualToAndBeGreaterOrEqualToSyntaxVisitor)) - { - var removeGreater = NodeReplacement.RemoveAndExtractArguments("BeGreaterOrEqualTo"); - var newExpression = GetNewExpression(expression, NodeReplacement.RemoveMethodBefore("BeGreaterOrEqualTo"), removeGreater); - - var renameLess = NodeReplacement.RenameAndExtractArguments("BeLessOrEqualTo", "BeInRange"); - newExpression = GetNewExpression(newExpression, renameLess); - - var arguments = removeGreater.Arguments.InsertRange(1, renameLess.Arguments); - - return GetNewExpression(newExpression, NodeReplacement.WithArguments("BeInRange", arguments)); - } - throw new System.InvalidOperationException($"Invalid visitor name - {properties.VisitorName}"); - } -} \ No newline at end of file diff --git a/src/FluentAssertions.Analyzers/Tips/Numerics/NumericShouldBeNegative.cs b/src/FluentAssertions.Analyzers/Tips/Numerics/NumericShouldBeNegative.cs deleted file mode 100644 index b70f7e09..00000000 --- a/src/FluentAssertions.Analyzers/Tips/Numerics/NumericShouldBeNegative.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Composition; - -namespace FluentAssertions.Analyzers; - -[DiagnosticAnalyzer(LanguageNames.CSharp)] -public class NumericShouldBeNegativeAnalyzer : NumericAnalyzer -{ - public const string DiagnosticId = Constants.Tips.Numeric.NumericShouldBeNegative; - public const string Category = Constants.Tips.Category; - - public const string Message = "Use .Should() followed by .BeNegative() instead."; - - protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true); - protected override IEnumerable Visitors - { - get - { - yield return new NumericShouldBeBeLessThan0SyntaxVisitor(); - } - } - - public class NumericShouldBeBeLessThan0SyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor - { - public NumericShouldBeBeLessThan0SyntaxVisitor() : base(MemberValidator.Should, MemberValidator.ArgumentIsLiteral("BeLessThan", 0)) - { - } - } -} - -[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(NumericShouldBeNegativeCodeFix)), Shared] -public class NumericShouldBeNegativeCodeFix : FluentAssertionsCodeFixProvider -{ - public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(NumericShouldBeNegativeAnalyzer.DiagnosticId); - - protected override ExpressionSyntax GetNewExpression(ExpressionSyntax expression, FluentAssertionsDiagnosticProperties properties) - { - return GetNewExpression(expression, NodeReplacement.RenameAndRemoveFirstArgument("BeLessThan", "BeNegative")); - } -} \ No newline at end of file diff --git a/src/FluentAssertions.Analyzers/Tips/Numerics/NumericShouldBePositive.cs b/src/FluentAssertions.Analyzers/Tips/Numerics/NumericShouldBePositive.cs deleted file mode 100644 index 7214f6a8..00000000 --- a/src/FluentAssertions.Analyzers/Tips/Numerics/NumericShouldBePositive.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Composition; - -namespace FluentAssertions.Analyzers; - -[DiagnosticAnalyzer(LanguageNames.CSharp)] -public class NumericShouldBePositiveAnalyzer : NumericAnalyzer -{ - public const string DiagnosticId = Constants.Tips.Numeric.NumericShouldBePositive; - public const string Category = Constants.Tips.Category; - - public const string Message = "Use .Should() followed by .BePositive() instead."; - - protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true); - protected override IEnumerable Visitors - { - get - { - yield return new NumericShouldBeBeGreaterThan0SyntaxVisitor(); - } - } - - public class NumericShouldBeBeGreaterThan0SyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor - { - public NumericShouldBeBeGreaterThan0SyntaxVisitor() : base(MemberValidator.Should, MemberValidator.ArgumentIsLiteral("BeGreaterThan", 0)) - { - } - } -} - -[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(NumericShouldBePositiveCodeFix)), Shared] -public class NumericShouldBePositiveCodeFix : FluentAssertionsCodeFixProvider -{ - public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(NumericShouldBePositiveAnalyzer.DiagnosticId); - - protected override ExpressionSyntax GetNewExpression(ExpressionSyntax expression, FluentAssertionsDiagnosticProperties properties) - { - return GetNewExpression(expression, NodeReplacement.RenameAndRemoveFirstArgument("BeGreaterThan", "BePositive")); - } -} \ No newline at end of file diff --git a/src/FluentAssertions.Analyzers/Utilities/HelpLinks.cs b/src/FluentAssertions.Analyzers/Utilities/HelpLinks.cs index e7ed4b4d..bf734a3b 100644 --- a/src/FluentAssertions.Analyzers/Utilities/HelpLinks.cs +++ b/src/FluentAssertions.Analyzers/Utilities/HelpLinks.cs @@ -12,12 +12,6 @@ static HelpLinks() { TypesHelpLinks = new Dictionary { - [typeof(NumericShouldBePositiveAnalyzer.NumericShouldBeBeGreaterThan0SyntaxVisitor)] = GetHelpLink("Comparable-and-Numerics-1"), - [typeof(NumericShouldBeNegativeAnalyzer.NumericShouldBeBeLessThan0SyntaxVisitor)] = GetHelpLink("Comparable-and-Numerics-2"), - [typeof(NumericShouldBeApproximatelyAnalyzer.MathAbsShouldBeLessOrEqualToSyntaxVisitor)] = string.Empty, // TODO: add to docs - [typeof(NumericShouldBeInRangeAnalyzer.BeGreaterOrEqualToAndBeLessOrEqualToSyntaxVisitor)] = string.Empty, // TODO: add to docs - [typeof(NumericShouldBeInRangeAnalyzer.BeLessOrEqualToAndBeGreaterOrEqualToSyntaxVisitor)] = string.Empty, // TODO: add to docs - [typeof(DictionaryShouldContainKeyAnalyzer.ContainsKeyShouldBeTrueSyntaxVisitor)] = GetHelpLink("Dictionaries-1"), [typeof(DictionaryShouldNotContainKeyAnalyzer.ContainsKeyShouldBeFalseSyntaxVisitor)] = GetHelpLink("Dictionaries-2"), [typeof(DictionaryShouldContainValueAnalyzer.ContainsValueShouldBeTrueSyntaxVisitor)] = GetHelpLink("Dictionaries-3"), diff --git a/src/FluentAssertions.Analyzers/Utilities/OperartionExtensions.cs b/src/FluentAssertions.Analyzers/Utilities/OperartionExtensions.cs index 70171420..7eb5b36d 100644 --- a/src/FluentAssertions.Analyzers/Utilities/OperartionExtensions.cs +++ b/src/FluentAssertions.Analyzers/Utilities/OperartionExtensions.cs @@ -53,17 +53,24 @@ public static bool IsSameArgumentReference(this IArgumentOperation argument1, IA } public static bool IsLiteralValue(this IArgumentOperation argument, T value) - => argument.Value is ILiteralOperation literal && literal.ConstantValue.HasValue && literal.ConstantValue.Value.Equals(value); + => UnwrapConversion(argument.Value) is ILiteralOperation literal && literal.ConstantValue.HasValue && literal.ConstantValue.Value.Equals(value); public static bool IsLiteralValue(this IArgumentOperation argument) - => argument.Value is ILiteralOperation literal && literal.ConstantValue.HasValue && literal.ConstantValue.Value is T; + => UnwrapConversion(argument.Value) is ILiteralOperation literal && literal.ConstantValue.HasValue && literal.ConstantValue.Value is T; public static bool IsReference(this IArgumentOperation argument) { - if (argument.Value is IConversionOperation conversion) - { - return conversion.Operand.Kind == OperationKind.LocalReference || conversion.Operand.Kind == OperationKind.ParameterReference; - } + var operation = UnwrapConversion(argument.Value); + return operation.Kind is OperationKind.LocalReference || operation.Kind is OperationKind.ParameterReference; + } - return argument.Value.Kind == OperationKind.LocalReference || argument.Value.Kind == OperationKind.ParameterReference; + public static bool IsReferenceOfType(this IArgumentOperation argument) + { + var current = UnwrapConversion(argument.Value); + return current switch + { + ILocalReferenceOperation local => local.Local.Type.SpecialType == SpecialType.System_Double, + IParameterReferenceOperation parameter => parameter.Parameter.Type.SpecialType == SpecialType.System_Double, + _ => false, + }; } public static bool IsLambda(this IArgumentOperation argument) @@ -91,4 +98,13 @@ public static bool TryGetChainedInvocationAfterAndConstraint(this IInvocationOpe chainedInvocation = null; return false; } + + private static IOperation UnwrapConversion(this IOperation operation) + { + return operation switch + { + IConversionOperation conversion => conversion.Operand, + _ => operation, + }; + } } \ No newline at end of file