diff --git a/src/FluentAssertions.Analyzers.Tests/Tips/XunitTests.cs b/src/FluentAssertions.Analyzers.Tests/Tips/XunitTests.cs index e2fc95b6..b81dcce7 100644 --- a/src/FluentAssertions.Analyzers.Tests/Tips/XunitTests.cs +++ b/src/FluentAssertions.Analyzers.Tests/Tips/XunitTests.cs @@ -57,6 +57,33 @@ public void AssertTrue_TestCodeFix(string oldAssertion, string newAssertion) public void AssertFalse_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix("bool actual", oldAssertion, newAssertion); + + [DataTestMethod] + [DataRow("Assert.Same(expected, actual);")] + [Implemented] + public void AssertSame_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic("object actual, object expected", assertion); + + [DataTestMethod] + [DataRow( + /* oldAssertion: */ "Assert.Same(expected, actual);", + /* newAssertion: */ "actual.Should().BeSameAs(expected);")] + [Implemented] + public void AssertSame_TestCodeFix(string oldAssertion, string newAssertion) + => VerifyCSharpFix("object actual, object expected", oldAssertion, newAssertion); + + [DataTestMethod] + [DataRow("Assert.NotSame(expected, actual);")] + [Implemented] + public void AssertNotSame_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic("object actual, object expected", assertion); + + [DataTestMethod] + [DataRow( + /* oldAssertion: */ "Assert.NotSame(expected, actual);", + /* newAssertion: */ "actual.Should().NotBeSameAs(expected);")] + [Implemented] + public void AssertNotSame_TestCodeFix(string oldAssertion, string newAssertion) + => VerifyCSharpFix("object actual, object expected", oldAssertion, newAssertion); + private void VerifyCSharpDiagnostic(string methodArguments, string assertion) where TDiagnosticAnalyzer : Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer, new() { var source = GenerateCode.XunitAssertion(methodArguments, assertion); diff --git a/src/FluentAssertions.Analyzers/Constants.cs b/src/FluentAssertions.Analyzers/Constants.cs index cc5f5193..7ccdbbef 100644 --- a/src/FluentAssertions.Analyzers/Constants.cs +++ b/src/FluentAssertions.Analyzers/Constants.cs @@ -120,6 +120,8 @@ public static class Xunit { public const string AssertTrue = nameof(AssertTrue); public const string AssertFalse = nameof(AssertFalse); + public const string AssertSame = nameof(AssertSame); + public const string AssertNotSame = nameof(AssertNotSame); } } diff --git a/src/FluentAssertions.Analyzers/Tips/Xunit/AssertNotSame.cs b/src/FluentAssertions.Analyzers/Tips/Xunit/AssertNotSame.cs new file mode 100644 index 00000000..c78701ba --- /dev/null +++ b/src/FluentAssertions.Analyzers/Tips/Xunit/AssertNotSame.cs @@ -0,0 +1,46 @@ +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.Xunit +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class AssertNotSameAnalyzer : XunitAnalyzer + { + public const string DiagnosticId = Constants.Tips.Xunit.AssertNotSame; + public const string Category = Constants.Tips.Category; + + public const string Message = "Use .Should().NotBeSameAs() instead."; + + protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true); + protected override IEnumerable Visitors + { + get + { + yield return new AssertNotSameSyntaxVisitor(); + } + } + + public class AssertNotSameSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor + { + public AssertNotSameSyntaxVisitor() : base(new MemberValidator("NotSame")) + { + } + } + } + + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AssertNotSameCodeFix)), Shared] + public class AssertNotSameCodeFix : XunitCodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(AssertNotSameAnalyzer.DiagnosticId); + + protected override ExpressionSyntax GetNewExpression(ExpressionSyntax expression, FluentAssertionsDiagnosticProperties properties) + { + return RenameMethodAndReorderActualExpectedAndReplaceWithSubjectShould(expression, "NotSame", "NotBeSameAs"); + } + } +} \ No newline at end of file diff --git a/src/FluentAssertions.Analyzers/Tips/Xunit/AssertSame.cs b/src/FluentAssertions.Analyzers/Tips/Xunit/AssertSame.cs new file mode 100644 index 00000000..3fd462f7 --- /dev/null +++ b/src/FluentAssertions.Analyzers/Tips/Xunit/AssertSame.cs @@ -0,0 +1,47 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; + +namespace FluentAssertions.Analyzers.Xunit +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class AssertSameAnalyzer : XunitAnalyzer + { + public const string DiagnosticId = Constants.Tips.Xunit.AssertSame; + public const string Category = Constants.Tips.Category; + + public const string Message = "Use .Should().BeSameAs() instead."; + + protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true); + protected override IEnumerable Visitors + { + get + { + yield return new AssertSameSyntaxVisitor(); + } + } + + public class AssertSameSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor + { + public AssertSameSyntaxVisitor() : base(new MemberValidator("Same")) + { + } + } + } + + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AssertSameCodeFix)), Shared] + public class AssertSameCodeFix : XunitCodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(AssertSameAnalyzer.DiagnosticId); + + protected override ExpressionSyntax GetNewExpression(ExpressionSyntax expression, FluentAssertionsDiagnosticProperties properties) + { + return RenameMethodAndReorderActualExpectedAndReplaceWithSubjectShould(expression, "Same", "BeSameAs"); + } + } +} \ No newline at end of file