diff --git a/src/FluentAssertions.Analyzers.Tests/Tips/CollectionTests.cs b/src/FluentAssertions.Analyzers.Tests/Tips/CollectionTests.cs index f98b395..99d0748 100644 --- a/src/FluentAssertions.Analyzers.Tests/Tips/CollectionTests.cs +++ b/src/FluentAssertions.Analyzers.Tests/Tips/CollectionTests.cs @@ -318,9 +318,30 @@ public class CollectionTests public void CollectionShouldHaveCount_CountShouldBe1_TestAnalyzer(string assertion) => VerifyCSharpDiagnosticCodeBlock(assertion, DiagnosticMetadata.CollectionShouldContainSingle_CountShouldBe1); [DataTestMethod] - [AssertionDiagnostic("actual.ToArray().Length.Should().Be(1{0}).And.ToString();")] - [Implemented] - public void CollectionShouldHaveCount_LengthShouldBe_TestAnalyzer(string assertion) => VerifyCSharpDiagnosticCodeBlock(assertion, DiagnosticMetadata.CollectionShouldHaveCount_LengthShouldBe); + [AssertionDiagnostic("(array.Count() + 1).Should().Be(0{0}).And.ToString();")] + [AssertionDiagnostic("(array.Count() + 1).Should().Be(1{0}).And.ToString();")] + [AssertionDiagnostic("(array.Count() + 1).Should().Be(expectedSize{0}).And.ToString();")] + [AssertionDiagnostic("(list.Count + 1).Should().Be(0{0}).And.ToString();")] + [AssertionDiagnostic("(list.Count + 1).Should().Be(1{0}).And.ToString();")] + [AssertionDiagnostic("(list.Count + 1).Should().Be(expectedSize{0}).And.ToString();")] + [Implemented] + public void CollectionShouldHaveCount_CountShouldBe_TestNoAnalyzer(string assertion) => DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(new StringBuilder() + .AppendLine("using System;") + .AppendLine("using System.Collections.Generic;") + .AppendLine("using System.Linq;") + .AppendLine("using FluentAssertions;") + .AppendLine("using FluentAssertions.Extensions;") + .AppendLine("namespace TestNamespace") + .AppendLine("{") + .AppendLine(" public class TestClass") + .AppendLine(" {") + .AppendLine(" public void TestMethod(string[] array, List list, int expectedSize)") + .AppendLine(" {") + .AppendLine(assertion) + .AppendLine(" }") + .AppendLine(" }") + .AppendLine("}") + .ToString()); [DataTestMethod] [AssertionDiagnostic(@"var array = new string[0, 0]; array.Length.Should().Be(0{0});")] @@ -610,6 +631,12 @@ public void CollectionShouldHaveCount_LengthShouldBe_TestNoAnalyzer(string asser [Implemented] public void CollectionShouldNotHaveSameCount_TestAnalyzer(string assertion) => VerifyCSharpDiagnosticCodeBlock(assertion, DiagnosticMetadata.CollectionShouldNotHaveSameCount_CountShouldNotBeOtherCollectionCount); + [DataTestMethod] + [AssertionDiagnostic("(actual.Count() + 1).Should().NotBe(unexpected.Count(){0});")] + [AssertionDiagnostic("actual.Count().ToString().Length.Should().NotBe(unexpected.Count(){0});")] + [Implemented] + public void CollectionShouldNotHaveSameCount_TestNotAnalyzer(string assertion) => DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(GenerateCode.GenericIListCodeBlockAssertion(assertion)); + [DataTestMethod] [AssertionCodeFix( oldAssertion: "actual.Count().Should().NotBe(unexpected.Count(){0});", @@ -742,6 +769,14 @@ public void CollectionShouldContainSingle_TestAnalyzer_GenericIEnumerableShouldR [Implemented] public void CollectionShouldHaveElementAt_ElementAtIndexShouldBe_TestAnalyzer(string assertion) => VerifyCSharpDiagnosticCodeBlock(assertion, DiagnosticMetadata.CollectionShouldHaveElementAt_ElementAtIndexShouldBe); + [DataTestMethod] + [AssertionDiagnostic("actual.ElementAt(k).BooleanProperty.Should().Be(expectedItem.BooleanProperty{0});")] + [AssertionDiagnostic("actual.ElementAt(6).BooleanProperty.Should().Be(expectedItem.BooleanProperty{0});")] + [AssertionDiagnostic("actual.AsEnumerable().ElementAt(k).BooleanProperty.Should().Be(expectedItem.BooleanProperty{0}).And.ToString();")] + [AssertionDiagnostic("actual.AsEnumerable().ElementAt(6).BooleanProperty.Should().Be(expectedItem.BooleanProperty{0}).And.ToString();")] + [Implemented] + public void CollectionShouldHaveElementAt_ElementAtIndexShouldBe_TestNoAnalyzer(string assertion) => DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(GenerateCode.GenericIListCodeBlockAssertion(assertion)); + [DataTestMethod] [AssertionDiagnostic("actual[k].Should().Be(expectedItem{0});")] [AssertionDiagnostic("actual[6].Should().Be(expectedItem{0});")] diff --git a/src/FluentAssertions.Analyzers/Tips/FluentAssertionsAnalyzer.cs b/src/FluentAssertions.Analyzers/Tips/FluentAssertionsAnalyzer.cs index b63b97c..4f2e644 100644 --- a/src/FluentAssertions.Analyzers/Tips/FluentAssertionsAnalyzer.cs +++ b/src/FluentAssertions.Analyzers/Tips/FluentAssertionsAnalyzer.cs @@ -301,7 +301,7 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs return; case "OnlyHaveUniqueItems" when assertion.IsContainedInType(metadata.GenericCollectionAssertionsOfT3): { - if (!invocation.TryGetFirstDescendent(out var invocationBeforeShould)) return; + if (!invocation.TryGetSingleArgumentAs(out var invocationBeforeShould)) return; switch (invocationBeforeShould.TargetMethod.Name) { case nameof(Enumerable.Select) when IsEnumerableMethodWithPredicate(invocationBeforeShould, metadata): @@ -312,7 +312,7 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs return; case "Be" when assertion.IsContainedInType(metadata.NumericAssertionsOfT2): { - if (invocation.TryGetFirstDescendent(out var invocationBeforeShould)) + if (invocation.TryGetSingleArgumentAs(out var invocationBeforeShould)) { switch (invocationBeforeShould.TargetMethod.Name) { @@ -334,8 +334,8 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs } } - var argument = invocation.Arguments[0].Value.UnwrapConversion(); - if (argument is IPropertyReferenceOperation propertyBeforeShould) + + if (invocation.TryGetSingleArgumentAs(out var propertyBeforeShould)) { switch (propertyBeforeShould.Property.Name) { @@ -370,7 +370,7 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs } case "Be" when assertion.IsContainedInType(metadata.ObjectAssertionsOfT2): { - if (invocation.TryGetFirstDescendent(out var invocationBeforeShould)) + if (invocation.TryGetSingleArgumentAs(out var invocationBeforeShould)) { switch (invocationBeforeShould.TargetMethod.Name) { @@ -402,7 +402,7 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, FluentAs return; case "NotBe" when assertion.IsContainedInType(metadata.NumericAssertionsOfT2): { - if (invocation.TryGetFirstDescendent(out var invocationBeforeShould)) + if (invocation.TryGetSingleArgumentAs(out var invocationBeforeShould)) { switch (invocationBeforeShould.TargetMethod.Name) { diff --git a/src/FluentAssertions.Analyzers/Utilities/OperartionExtensions.cs b/src/FluentAssertions.Analyzers/Utilities/OperartionExtensions.cs index 9c048f6..22a9c12 100644 --- a/src/FluentAssertions.Analyzers/Utilities/OperartionExtensions.cs +++ b/src/FluentAssertions.Analyzers/Utilities/OperartionExtensions.cs @@ -192,6 +192,18 @@ public static bool TryGetChainedInvocationAfterAndConstraint(this IInvocationOpe return false; } + public static bool TryGetSingleArgumentAs(this IInvocationOperation invocation, out TOperation operation) + { + if (invocation.Arguments.Length is 1 && invocation.Arguments[0].Value.UnwrapConversion() is TOperation op) + { + operation = op; + return true; + } + + operation = default; + return false; + } + public static IOperation UnwrapConversion(this IOperation operation) { return operation switch