From 611cf405f043b1cdc4a804ced38a0d635890e916 Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Mon, 21 Oct 2024 11:08:49 +0300 Subject: [PATCH] bugfix: improve CollectionShouldHaveCountGreaterOrEqualTo_CountShouldBeGreaterOrEqualTo detection --- .../Tips/CollectionTests.cs | 26 +++++++++++++++++++ .../Tips/FluentAssertionsAnalyzer.Utils.cs | 4 +++ .../Tips/FluentAssertionsAnalyzer.cs | 13 +++++++++- 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/FluentAssertions.Analyzers.Tests/Tips/CollectionTests.cs b/src/FluentAssertions.Analyzers.Tests/Tips/CollectionTests.cs index 50edbcb..812631d 100644 --- a/src/FluentAssertions.Analyzers.Tests/Tips/CollectionTests.cs +++ b/src/FluentAssertions.Analyzers.Tests/Tips/CollectionTests.cs @@ -451,11 +451,25 @@ public void CollectionShouldHaveCount_LengthShouldBe_TestNoAnalyzer(string asser [DataTestMethod] [AssertionDiagnostic("actual.Count().Should().BeGreaterOrEqualTo(k{0});")] [AssertionDiagnostic("actual.Count().Should().BeGreaterOrEqualTo(6{0});")] + [AssertionDiagnostic("actual.Count.Should().BeGreaterOrEqualTo(k{0});")] + [AssertionDiagnostic("actual.Count.Should().BeGreaterOrEqualTo(6{0});")] + [AssertionDiagnostic("actual.ToArray().Length.Should().BeGreaterOrEqualTo(k{0});")] + [AssertionDiagnostic("actual.ToArray().Length.Should().BeGreaterOrEqualTo(6{0});")] [AssertionDiagnostic("actual.AsEnumerable().Count().Should().BeGreaterOrEqualTo(k{0}).And.ToString();")] [AssertionDiagnostic("actual.AsEnumerable().Count().Should().BeGreaterOrEqualTo(6{0}).And.ToString();")] [Implemented] public void CollectionShouldHaveCountGreaterOrEqualTo_TestAnalyzer(string assertion) => VerifyCSharpDiagnosticCodeBlock(assertion, DiagnosticMetadata.CollectionShouldHaveCountGreaterOrEqualTo_CountShouldBeGreaterOrEqualTo); + [DataTestMethod] + [AssertionDiagnostic("(actual.Count() + 1).Should().BeGreaterOrEqualTo(k{0});")] + [AssertionDiagnostic("(actual.Count() + 1).Should().BeGreaterOrEqualTo(6{0});")] + [AssertionDiagnostic("(actual.Count + 1).Should().BeGreaterOrEqualTo(k{0});")] + [AssertionDiagnostic("(actual.Count + 1).Should().BeGreaterOrEqualTo(6{0});")] + [AssertionDiagnostic("(actual.ToArray().Length + 1).Should().BeGreaterOrEqualTo(k{0});")] + [AssertionDiagnostic("(actual.ToArray().Length + 1).Should().BeGreaterOrEqualTo(6{0});")] + [Implemented] + public void CollectionShouldHaveCountGreaterOrEqualTo_TestNoAnalyzer(string assertion) => DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(GenerateCode.GenericIListCodeBlockAssertion(assertion)); + [DataTestMethod] [AssertionCodeFix( oldAssertion: "actual.Count().Should().BeGreaterOrEqualTo(k{0});", @@ -463,6 +477,18 @@ public void CollectionShouldHaveCount_LengthShouldBe_TestNoAnalyzer(string asser [AssertionCodeFix( oldAssertion: "actual.Count().Should().BeGreaterOrEqualTo(6{0});", newAssertion: "actual.Should().HaveCountGreaterOrEqualTo(6{0});")] + [AssertionCodeFix( + oldAssertion: "actual.Count.Should().BeGreaterOrEqualTo(k{0});", + newAssertion: "actual.Should().HaveCountGreaterOrEqualTo(k{0});")] + [AssertionCodeFix( + oldAssertion: "actual.Count.Should().BeGreaterOrEqualTo(6{0});", + newAssertion: "actual.Should().HaveCountGreaterOrEqualTo(6{0});")] + [AssertionCodeFix( + oldAssertion: "actual.ToArray().Length.Should().BeGreaterOrEqualTo(k{0});", + newAssertion: "actual.ToArray().Should().HaveCountGreaterOrEqualTo(k{0});")] + [AssertionCodeFix( + oldAssertion: "actual.ToArray().Length.Should().BeGreaterOrEqualTo(6{0});", + newAssertion: "actual.ToArray().Should().HaveCountGreaterOrEqualTo(6{0});")] [AssertionCodeFix( oldAssertion: "actual.AsEnumerable().Count().Should().BeGreaterOrEqualTo(k{0}).And.ToString();", newAssertion: "actual.AsEnumerable().Should().HaveCountGreaterOrEqualTo(k{0}).And.ToString();")] diff --git a/src/FluentAssertions.Analyzers/Tips/FluentAssertionsAnalyzer.Utils.cs b/src/FluentAssertions.Analyzers/Tips/FluentAssertionsAnalyzer.Utils.cs index 0de31b1..e173e4a 100644 --- a/src/FluentAssertions.Analyzers/Tips/FluentAssertionsAnalyzer.Utils.cs +++ b/src/FluentAssertions.Analyzers/Tips/FluentAssertionsAnalyzer.Utils.cs @@ -72,6 +72,8 @@ public FluentAssertionsMetadata(Compilation compilation) IReadonlyDictionaryOfT2 = compilation.GetTypeByMetadataName(typeof(IReadOnlyDictionary<,>).FullName); IListOfT = compilation.GetTypeByMetadataName(typeof(IList<>).FullName); IReadonlyListOfT = compilation.GetTypeByMetadataName(typeof(IReadOnlyList<>).FullName); + ICollectionOfT = compilation.GetTypeByMetadataName(typeof(ICollection<>).FullName); + IReadonlyCollectionOfT = compilation.GetTypeByMetadataName(typeof(IReadOnlyCollection<>).FullName); Enumerable = compilation.GetTypeByMetadataName(typeof(Enumerable).FullName); IEnumerable = compilation.GetTypeByMetadataName(typeof(IEnumerable).FullName); Math = compilation.GetTypeByMetadataName(typeof(Math).FullName); @@ -91,6 +93,8 @@ public FluentAssertionsMetadata(Compilation compilation) public INamedTypeSymbol IReadonlyDictionaryOfT2 { get; } public INamedTypeSymbol IListOfT { get; } public INamedTypeSymbol IReadonlyListOfT { get; } + public INamedTypeSymbol ICollectionOfT { get; } + public INamedTypeSymbol IReadonlyCollectionOfT { get; } public INamedTypeSymbol BooleanAssertionsOfT1 { get; } public INamedTypeSymbol NumericAssertionsOfT2 { get; } public INamedTypeSymbol Enumerable { get; } diff --git a/src/FluentAssertions.Analyzers/Tips/FluentAssertionsAnalyzer.cs b/src/FluentAssertions.Analyzers/Tips/FluentAssertionsAnalyzer.cs index f76af54..add11ac 100644 --- a/src/FluentAssertions.Analyzers/Tips/FluentAssertionsAnalyzer.cs +++ b/src/FluentAssertions.Analyzers/Tips/FluentAssertionsAnalyzer.cs @@ -423,7 +423,7 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs return; case "BeGreaterOrEqualTo" when assertion.IsContainedInType(metadata.NumericAssertionsOfT2): { - if (invocation.TryGetFirstDescendent(out var invocationBeforeShould)) + if (invocation.TryGetSingleArgumentAs(out var invocationBeforeShould)) { switch (invocationBeforeShould.TargetMethod.Name) { @@ -434,6 +434,17 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs } } + if (invocation.TryGetSingleArgumentAs(out var propertyBeforeShould)) + { + switch (propertyBeforeShould.Property.Name) + { + case nameof(Array.Length) when propertyBeforeShould.IsContainedInType(SpecialType.System_Array): + case nameof(List.Count) when propertyBeforeShould.ImplementsOrIsInterface(SpecialType.System_Collections_Generic_ICollection_T): + context.ReportDiagnostic(CreateDiagnostic(assertion, DiagnosticMetadata.CollectionShouldHaveCountGreaterOrEqualTo_CountShouldBeGreaterOrEqualTo)); + return; + } + } + if (assertion.TryGetChainedInvocationAfterAndConstraint("BeLessOrEqualTo", out var chainedInvocation)) { if (!assertion.HasEmptyBecauseAndReasonArgs(startingIndex: 1) && !chainedInvocation.HasEmptyBecauseAndReasonArgs(startingIndex: 1)) return;