From d1228eae281d4af26a800c810cbe686309cd600b Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Wed, 28 Jun 2023 07:15:47 +0300 Subject: [PATCH] fix: support first argument being the null keyword (#201) --- .../Tips/MsTestTests.cs | 17 ++++++++++++-- .../Tips/MsTest/AssertAreEqual.cs | 22 +++++++++++++++---- .../Utilities/ArgumentValidator.cs | 2 +- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/FluentAssertions.Analyzers.Tests/Tips/MsTestTests.cs b/src/FluentAssertions.Analyzers.Tests/Tips/MsTestTests.cs index 9c426967..e1d6bf3c 100644 --- a/src/FluentAssertions.Analyzers.Tests/Tips/MsTestTests.cs +++ b/src/FluentAssertions.Analyzers.Tests/Tips/MsTestTests.cs @@ -326,14 +326,27 @@ public void AssertOptionalIntAreEqual_TestCodeFix(string oldAssertion, string ne [AssertionDataTestMethod] [AssertionDiagnostic("Assert.AreEqual(actual, null{0});")] [Implemented] - public void AssertOptionalIntAndNullAreEqual_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic("int? actual", assertion); + public void AssertOptionalIntAndNullAreEqual1_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic("int? actual", assertion); [AssertionDataTestMethod] [AssertionCodeFix( oldAssertion: "Assert.AreEqual(actual, null{0});", newAssertion: "actual.Should().BeNull({0});")] [Implemented] - public void AssertOptionalIntAndNullAreEqual_TestCodeFix(string oldAssertion, string newAssertion) + public void AssertOptionalIntAndNullAreEqual1_TestCodeFix(string oldAssertion, string newAssertion) + => VerifyCSharpFix("int? actual", oldAssertion, newAssertion); + + [AssertionDataTestMethod] + [AssertionDiagnostic("Assert.AreEqual(null, actual{0});")] + [Implemented] + public void AssertOptionalIntAndNullAreEqual2_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic("int? actual", assertion); + + [AssertionDataTestMethod] + [AssertionCodeFix( + oldAssertion: "Assert.AreEqual(null, actual{0});", + newAssertion: "actual.Should().BeNull({0});")] + [Implemented] + public void AssertOptionalIntAndNullAreEqual2_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix("int? actual", oldAssertion, newAssertion); [AssertionDataTestMethod] diff --git a/src/FluentAssertions.Analyzers/Tips/MsTest/AssertAreEqual.cs b/src/FluentAssertions.Analyzers/Tips/MsTest/AssertAreEqual.cs index 589adad1..60676b2f 100644 --- a/src/FluentAssertions.Analyzers/Tips/MsTest/AssertAreEqual.cs +++ b/src/FluentAssertions.Analyzers/Tips/MsTest/AssertAreEqual.cs @@ -27,7 +27,8 @@ protected override IEnumerable Visitors yield return new AssertFloatAreEqualWithDeltaSyntaxVisitor(); yield return new AssertDoubleAreEqualWithDeltaSyntaxVisitor(); yield return new AssertStringAreEqualSyntaxVisitor(); - yield return new AssertObjectAreEqualNullSyntaxVisitor(); + yield return new AssertObjectAreEqualNull1SyntaxVisitor(); + yield return new AssertObjectAreEqualNull2SyntaxVisitor(); yield return new AssertObjectAreEqualSyntaxVisitor(); } } @@ -70,9 +71,9 @@ public AssertStringAreEqualSyntaxVisitor() : base( } } - public class AssertObjectAreEqualNullSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor + public class AssertObjectAreEqualNull1SyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor { - public AssertObjectAreEqualNullSyntaxVisitor() : base( + public AssertObjectAreEqualNull1SyntaxVisitor() : base( MemberValidator.ArgumentsMatch("AreEqual", ArgumentValidator.IsIdentifier(), ArgumentValidator.IsNull())) @@ -80,6 +81,16 @@ public AssertObjectAreEqualNullSyntaxVisitor() : base( } } + public class AssertObjectAreEqualNull2SyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor + { + public AssertObjectAreEqualNull2SyntaxVisitor() : base( + MemberValidator.ArgumentsMatch("AreEqual", + ArgumentValidator.IsNull(), + ArgumentValidator.IsIdentifier())) + { + } + } + // public static void AreEqual(T expected, T actual) // public static void AreEqual(object expected, object actual) public class AssertObjectAreEqualSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor @@ -107,9 +118,12 @@ protected override async Task GetNewExpressionAsync(Expression case nameof(AssertAreEqualAnalyzer.AssertStringAreEqualSyntaxVisitor): var semanticModel = await document.GetSemanticModelAsync(cancellationToken); return GetNewExpressionForAreNotEqualOrAreEqualStrings(expression, semanticModel, "AreEqual", "Be", "BeEquivalentTo"); - case nameof(AssertAreEqualAnalyzer.AssertObjectAreEqualNullSyntaxVisitor): + case nameof(AssertAreEqualAnalyzer.AssertObjectAreEqualNull1SyntaxVisitor): expression = RenameMethodAndReplaceWithSubjectShould(expression, "AreEqual", "BeNull"); return GetNewExpression(expression, NodeReplacement.RemoveFirstArgument("BeNull")); + case nameof(AssertAreEqualAnalyzer.AssertObjectAreEqualNull2SyntaxVisitor): + expression = GetNewExpression(expression, NodeReplacement.RemoveFirstArgument("AreEqual")); + return RenameMethodAndReplaceWithSubjectShould(expression, "AreEqual", "BeNull"); default: throw new System.InvalidOperationException($"Invalid visitor name - {properties.VisitorName}"); } diff --git a/src/FluentAssertions.Analyzers/Utilities/ArgumentValidator.cs b/src/FluentAssertions.Analyzers/Utilities/ArgumentValidator.cs index 705d6f85..968aaf2a 100644 --- a/src/FluentAssertions.Analyzers/Utilities/ArgumentValidator.cs +++ b/src/FluentAssertions.Analyzers/Utilities/ArgumentValidator.cs @@ -10,7 +10,7 @@ public class ArgumentValidator public static ArgumentPredicate IsIdentifier() => (argument, semanticModel) => argument.Expression.IsKind(SyntaxKind.IdentifierName); public static ArgumentPredicate IsType(Func typeSelector) - => (argument, semanticModel) => semanticModel.GetTypeInfo(argument.Expression).Type.Equals(typeSelector(semanticModel), SymbolEqualityComparer.Default); + => (argument, semanticModel) => semanticModel.GetTypeInfo(argument.Expression).Type?.Equals(typeSelector(semanticModel), SymbolEqualityComparer.Default) ?? false; public static ArgumentPredicate IsNull() => (argument, semanticModel) => argument.Expression is LiteralExpressionSyntax literal && literal.Token.IsKind(SyntaxKind.NullKeyword); }