diff --git a/FluentAssertions.BestPractices.sln b/FluentAssertions.BestPractices.sln deleted file mode 100644 index ee0aac7e..00000000 --- a/FluentAssertions.BestPractices.sln +++ /dev/null @@ -1,41 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.16 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FluentAssertions.BestPractices.Tests", "FluentAssertions.BestPractices.Tests\FluentAssertions.BestPractices.Tests.csproj", "{979824BD-5936-4969-B43B-BF613B3C0C5F}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{1D2F0A0B-7B98-49D6-BD83-E750A2DD7FD0}" - ProjectSection(SolutionItems) = preProject - .gitattributes = .gitattributes - .gitignore = .gitignore - build.cake = build.cake - build.ps1 = build.ps1 - build.sh = build.sh - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FluentAssertions.BestPractices", "FluentAssertions.BestPractices\FluentAssertions.BestPractices.csproj", "{3BA672F7-00D8-4E77-89A0-D46DD99D35AA}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {979824BD-5936-4969-B43B-BF613B3C0C5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {979824BD-5936-4969-B43B-BF613B3C0C5F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {979824BD-5936-4969-B43B-BF613B3C0C5F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {979824BD-5936-4969-B43B-BF613B3C0C5F}.Release|Any CPU.Build.0 = Release|Any CPU - {3BA672F7-00D8-4E77-89A0-D46DD99D35AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3BA672F7-00D8-4E77-89A0-D46DD99D35AA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3BA672F7-00D8-4E77-89A0-D46DD99D35AA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3BA672F7-00D8-4E77-89A0-D46DD99D35AA}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {4BF3D005-625C-4CEC-B3FB-298B956402BE} - EndGlobalSection -EndGlobal diff --git a/build.cake b/build.cake index ea0d143f..2f2d42fd 100644 --- a/build.cake +++ b/build.cake @@ -104,8 +104,7 @@ Task("Default") .IsDependentOn("Update-Version") .IsDependentOn("Build") .IsDependentOn("Run-Unit-Tests") - .IsDependentOn("Pack") - .IsDependentOn("Publish-NuGet"); + .IsDependentOn("Pack"); ////////////////////////////////////////////////////////////////////// // EXECUTION diff --git a/src/FluentAssertions.BestPractices.Tests/Tips/CollectionTests.cs b/src/FluentAssertions.BestPractices.Tests/Tips/CollectionTests.cs index 14f46935..c614d230 100644 --- a/src/FluentAssertions.BestPractices.Tests/Tips/CollectionTests.cs +++ b/src/FluentAssertions.BestPractices.Tests/Tips/CollectionTests.cs @@ -259,14 +259,18 @@ public class CollectionTests [AssertionDataTestMethod] [AssertionDiagnostic("actual.Should().NotBeNull().And.NotBeEmpty({0});")] - [NotImplemented] + [AssertionDiagnostic("actual.Should().NotBeEmpty().And.NotBeNull({0});")] + [Implemented] public void CollectionShouldNotBeNullOrEmpty_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion); [AssertionDataTestMethod] [AssertionCodeFix( - oldAssertion: "actual.Should().NotBeNull({0}).And.NotBeEmpty({0});", - newAssertion: "actual.Should().NotBeNullOrEmpty({0});")] - [NotImplemented] + oldAssertion: "actual.Should().NotBeNull().And.NotBeEmpty({0});", + newAssertion: "actual.Should().NotBeNullOrEmpty({0});")] + [AssertionCodeFix( + oldAssertion: "actual.Should().NotBeEmpty().And.NotBeNull({0});", + newAssertion: "actual.Should().NotBeNullOrEmpty({0});")] + [Implemented] public void CollectionShouldNotBeNullOrEmpty_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix(oldAssertion, newAssertion); [AssertionDataTestMethod] @@ -326,15 +330,15 @@ public class CollectionTests public void CollectionShouldBeInDescendingOrder_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix(oldAssertion, newAssertion); [AssertionDataTestMethod] - [AssertionDiagnostic("actual.Select(e1 => e1.SomeProperty).Should().Equal(expected.Select(e2 => e2.SomeProperty){0});")] - [NotImplemented] + [AssertionDiagnostic("actual.Select(e1 => e1.BooleanProperty).Should().Equal(expected.Select(e2 => e2.BooleanProperty){0});")] + [Implemented] public void CollectionShouldEqualOtherCollectionByComparer_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion); [AssertionDataTestMethod] [AssertionCodeFix( - oldAssertion: "actual.Select(e1 => e1.SomeProperty).Should().Equal(expected.Select(e2 => e2.SomeProperty){0});", - newAssertion: "actual.Should().Equal(expected, (e1, e2) => e1.SomeProperty == e2.SomeProperty{0});")] - [NotImplemented] + oldAssertion: "actual.Select(e1 => e1.BooleanProperty).Should().Equal(expected.Select(e2 => e2.BooleanProperty){0});", + newAssertion: "actual.Should().Equal(expected, (e1, e2) => e1.BooleanProperty == e2.BooleanProperty{0});")] + [Implemented] public void CollectionShouldEqualOtherCollectionByComparer_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix(oldAssertion, newAssertion); [AssertionDataTestMethod] @@ -344,8 +348,8 @@ public class CollectionTests [AssertionDataTestMethod] [AssertionCodeFix( - oldAssertion: "actual.Intersect(expected).Should().BeEmpty({0});", - newAssertion: "actual.Should().NotIntersectWith(expected{0});")] + oldAssertion: "actual.Intersect(expected).Should().BeEmpty({0});", + newAssertion: "actual.Should().NotIntersectWith(expected{0});")] [Implemented] public void CollectionShouldNotIntersectWith_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix(oldAssertion, newAssertion); @@ -376,30 +380,33 @@ public class CollectionTests [AssertionDataTestMethod] [AssertionDiagnostic("actual.Should().HaveSameCount(actual.Distinct(){0});")] - [NotImplemented] + [Implemented] public void CollectionShouldOnlyHaveUniqueItems_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion); [AssertionDataTestMethod] [AssertionCodeFix( oldAssertion: "actual.Should().HaveSameCount(actual.Distinct(){0});", newAssertion: "actual.Should().OnlyHaveUniqueItems({0});")] - [NotImplemented] + [Implemented] public void CollectionShouldOnlyHaveUniqueItems_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix(oldAssertion, newAssertion); [AssertionDataTestMethod] [AssertionDiagnostic("actual.Select(x => x.BooleanProperty).Should().OnlyHaveUniqueItems({0});")] - [NotImplemented] + [Implemented] public void CollectionShouldOnlyHaveUniqueItemsByComparer_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion); [AssertionDataTestMethod] [AssertionCodeFix( oldAssertion: "actual.Select(x => x.BooleanProperty).Should().OnlyHaveUniqueItems({0});", newAssertion: "actual.Should().OnlyHaveUniqueItems(x => x.BooleanProperty{0});")] - [NotImplemented] + [Implemented] + [Ignore("Will be available in Fluent Assertions 5.0")] public void CollectionShouldOnlyHaveUniqueItemsByComparer_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix(oldAssertion, newAssertion); + [AssertionDataTestMethod] [AssertionDiagnostic("actual.FirstOrDefault().Should().BeNull({0});")] [NotImplemented] + [Ignore("What Should Happen?")] public void CollectionShouldHaveElementAt0Null_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion); [AssertionDataTestMethod] @@ -407,6 +414,7 @@ public class CollectionTests oldAssertion: "actual.FirstOrDefault().Should().BeNull({0});", newAssertion: "actual.Should().HaveElementAt(0, null{0});")] [NotImplemented] + [Ignore("What Should Happen?")] public void CollectionShouldHaveElementAt0Null_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix(oldAssertion, newAssertion); private void VerifyCSharpDiagnostic(string sourceAssersion) where TDiagnosticAnalyzer : Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer, new() diff --git a/src/FluentAssertions.BestPractices/FluentAssertions.BestPractices.nuspec b/src/FluentAssertions.BestPractices/FluentAssertions.BestPractices.nuspec index 2a80fe47..688a6845 100644 --- a/src/FluentAssertions.BestPractices/FluentAssertions.BestPractices.nuspec +++ b/src/FluentAssertions.BestPractices/FluentAssertions.BestPractices.nuspec @@ -3,7 +3,7 @@ FluentAssertions.BestPractices Fluent Assertions Best Practice - 0.2.1 + 0.3.0 Meir Blachman Meir Blachman diff --git a/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldEqualOtherCollectionByComparer.cs b/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldEqualOtherCollectionByComparer.cs index b558579d..7633e6e2 100644 --- a/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldEqualOtherCollectionByComparer.cs +++ b/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldEqualOtherCollectionByComparer.cs @@ -1,5 +1,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using System.Collections.Generic; @@ -14,32 +15,62 @@ public class CollectionShouldEqualOtherCollectionByComparerAnalyzer : FluentAsse public const string DiagnosticId = Constants.Tips.Collections.CollectionShouldEqualOtherCollectionByComparer; public const string Category = Constants.Tips.Category; - public const string Message = "Use {0} .Should() followed by ### instead."; + public const string Message = "Use {0} .Should() followed by .Equal() instead."; protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true); - protected override Diagnostic AnalyzeExpressionStatement(ExpressionStatementSyntax statement) + protected override IEnumerable<(FluentAssertionsCSharpSyntaxVisitor, BecauseArgumentsSyntaxVisitor)> Visitors { - return null; - var visitor = new CollectionShouldEqualOtherCollectionByComparerSyntaxVisitor(); - statement.Accept(visitor); + get + { + yield return (new SelectShouldEqualOtherCollectionSelectSyntaxVisitor(), new BecauseArgumentsSyntaxVisitor("Equal", 1)); + } + } + + private class SelectShouldEqualOtherCollectionSelectSyntaxVisitor : FluentAssertionsWithArgumentsCSharpSyntaxVisitor + { + private ExpressionSyntax _lambdaArgument; + private string _otherVariable; - if (visitor.IsValid) + protected override bool AreArgumentsValid() { - var properties = new Dictionary + if (Arguments.TryGetValue(("Select", 0), out var selectArgument) && selectArgument is SimpleLambdaExpressionSyntax select + && Arguments.TryGetValue(("Equal", 0), out var expectedArgument) && expectedArgument is InvocationExpressionSyntax expected) { - [Constants.DiagnosticProperties.VariableName] = visitor.VariableName, - [Constants.DiagnosticProperties.Title] = Title - }.ToImmutableDictionary(); - throw new System.NotImplementedException(); + var visitor = new SelectSyntaxVisitor(); + expected.Accept(visitor); + + if (visitor.IsValid) + { + _otherVariable = visitor.VariableName; + _lambdaArgument = SyntaxFactory.ParenthesizedLambdaExpression( + parameterList: SyntaxFactory.ParameterList().AddParameters(select.Parameter, visitor.Lambda.Parameter), + body: SyntaxFactory.BinaryExpression(SyntaxKind.EqualsExpression, + left: (ExpressionSyntax)select.Body, + right: (ExpressionSyntax)visitor.Lambda.Body) + ).NormalizeWhitespace(); + return true; + } + } + return false; + } + + public SelectShouldEqualOtherCollectionSelectSyntaxVisitor() : base("Select", "Should", "Equal") + { + } - return Diagnostic.Create( - descriptor: Rule, - location: statement.Expression.GetLocation(), - properties: properties, - messageArgs: visitor.VariableName); + public override ImmutableDictionary ToDiagnosticProperties() => base.ToDiagnosticProperties() + .Add(Constants.DiagnosticProperties.LambdaString, _lambdaArgument.ToFullString()) + .Add(Constants.DiagnosticProperties.ArgumentString, _otherVariable); + + + private class SelectSyntaxVisitor : FluentAssertionsWithLambdaArgumentCSharpSyntaxVisitor + { + protected override string MethodContainingLambda => "Select"; + public SelectSyntaxVisitor() : base("Select") + { + } } - return null; } } @@ -49,16 +80,6 @@ public class CollectionShouldEqualOtherCollectionByComparerCodeFix : FluentAsser public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(CollectionShouldEqualOtherCollectionByComparerAnalyzer.DiagnosticId); protected override StatementSyntax GetNewStatement(FluentAssertionsDiagnosticProperties properties) - { - throw new System.NotImplementedException(); - } - } - - public class CollectionShouldEqualOtherCollectionByComparerSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor - { - public CollectionShouldEqualOtherCollectionByComparerSyntaxVisitor() : base("###") - { - throw new System.NotImplementedException(); - } + => SyntaxFactory.ParseStatement($"{properties.VariableName}.Should().Equal({properties.ArgumentString}, {properties.CombineWithBecauseArgumentsString(properties.LambdaString)});"); } } diff --git a/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldHaveElementAt.cs b/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldHaveElementAt.cs index a51e229a..eb06947e 100644 --- a/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldHaveElementAt.cs +++ b/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldHaveElementAt.cs @@ -30,7 +30,7 @@ public class CollectionShouldHaveElementAtAnalyzer : FluentAssertionsAnalyzer private class ElementAtIndexShouldBeSyntaxVisitor : FluentAssertionsWithArgumentsCSharpSyntaxVisitor { - protected override bool AreArgumentsValid => + protected override bool AreArgumentsValid() => Arguments.TryGetValue(("ElementAt", 0), out ExpressionSyntax index) && (index is LiteralExpressionSyntax || index is IdentifierNameSyntax) && Arguments.TryGetValue(("Be", 0), out ExpressionSyntax expectedItem) && (expectedItem is LiteralExpressionSyntax || expectedItem is IdentifierNameSyntax); @@ -45,7 +45,7 @@ public override ImmutableDictionary ToDiagnosticProperties() } private class IndexerShouldBeSyntaxVisitor : FluentAssertionsWithArgumentsCSharpSyntaxVisitor { - protected override bool AreArgumentsValid => + protected override bool AreArgumentsValid() => Arguments.TryGetValue(("[]", 0), out ExpressionSyntax index) && (index is LiteralExpressionSyntax || index is IdentifierNameSyntax) && Arguments.TryGetValue(("Be", 0), out ExpressionSyntax expectedItem) && (expectedItem is LiteralExpressionSyntax || expectedItem is IdentifierNameSyntax); @@ -60,7 +60,7 @@ public override ImmutableDictionary ToDiagnosticProperties() private class SkipFirstShouldBeSyntaxVisitor : FluentAssertionsWithArgumentsCSharpSyntaxVisitor { - protected override bool AreArgumentsValid => + protected override bool AreArgumentsValid() => Arguments.TryGetValue(("Skip", 0), out ExpressionSyntax index) && (index is LiteralExpressionSyntax || index is IdentifierNameSyntax) && Arguments.TryGetValue(("Be", 0), out ExpressionSyntax expectedItem) && (expectedItem is LiteralExpressionSyntax || expectedItem is IdentifierNameSyntax); diff --git a/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldNotBeNullOrEmpty.cs b/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldNotBeNullOrEmpty.cs index eb53e632..72229770 100644 --- a/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldNotBeNullOrEmpty.cs +++ b/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldNotBeNullOrEmpty.cs @@ -1,5 +1,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using System.Collections.Generic; @@ -18,28 +19,26 @@ public class CollectionShouldNotBeNullOrEmptyAnalyzer : FluentAssertionsAnalyzer protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true); - protected override Diagnostic AnalyzeExpressionStatement(ExpressionStatementSyntax statement) + protected override IEnumerable<(FluentAssertionsCSharpSyntaxVisitor, BecauseArgumentsSyntaxVisitor)> Visitors { - return null; - var visitor = new CollectionShouldNotBeNullOrEmptySyntaxVisitor(); - statement.Accept(visitor); - - if (visitor.IsValid) + get { - var properties = new Dictionary - { - [Constants.DiagnosticProperties.VariableName] = visitor.VariableName, - [Constants.DiagnosticProperties.Title] = Title - }.ToImmutableDictionary(); - throw new System.NotImplementedException(); + yield return (new ShouldNotBeNullAndNotBeEmptySyntaxVisitor(), new BecauseArgumentsSyntaxVisitor("NotBeEmpty", 0)); + yield return (new ShouldNotBeEmptyAndNotBeNullSyntaxVisitor(), new BecauseArgumentsSyntaxVisitor("NotBeNull", 0)); + } + } - return Diagnostic.Create( - descriptor: Rule, - location: statement.Expression.GetLocation(), - properties: properties, - messageArgs: visitor.VariableName); + private class ShouldNotBeNullAndNotBeEmptySyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor + { + public ShouldNotBeNullAndNotBeEmptySyntaxVisitor() : base("Should", "NotBeNull", "And", "NotBeEmpty") + { + } + } + private class ShouldNotBeEmptyAndNotBeNullSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor + { + public ShouldNotBeEmptyAndNotBeNullSyntaxVisitor() : base("Should", "NotBeEmpty", "And", "NotBeNull") + { } - return null; } } @@ -49,16 +48,6 @@ public class CollectionShouldNotBeNullOrEmptyCodeFix : FluentAssertionsCodeFixPr public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(CollectionShouldNotBeNullOrEmptyAnalyzer.DiagnosticId); protected override StatementSyntax GetNewStatement(FluentAssertionsDiagnosticProperties properties) - { - throw new System.NotImplementedException(); - } - } - - public class CollectionShouldNotBeNullOrEmptySyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor - { - public CollectionShouldNotBeNullOrEmptySyntaxVisitor() : base("###") - { - throw new System.NotImplementedException(); - } + => SyntaxFactory.ParseStatement($"{properties.VariableName}.Should().NotBeNullOrEmpty({properties.BecauseArgumentsString});"); } } diff --git a/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldNotContainProperty.cs b/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldNotContainProperty.cs index 04a60d6a..29f48e2f 100644 --- a/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldNotContainProperty.cs +++ b/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldNotContainProperty.cs @@ -46,7 +46,7 @@ public WhereShouldBeEmptySyntaxVisitor() : base("Where", "Should", "BeEmpty") private class ShouldOnlyContainNotSyntaxVisitor : FluentAssertionsWithLambdaArgumentCSharpSyntaxVisitor { protected override string MethodContainingLambda => "OnlyContain"; - protected override SimpleLambdaExpressionSyntax Lambda => ReverseLambda(base.Lambda); + public override SimpleLambdaExpressionSyntax Lambda => ReverseLambda(base.Lambda); public ShouldOnlyContainNotSyntaxVisitor() : base("Should", "OnlyContain") { diff --git a/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldOnlyHaveUniqueItems.cs b/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldOnlyHaveUniqueItems.cs index 872c160c..7022302d 100644 --- a/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldOnlyHaveUniqueItems.cs +++ b/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldOnlyHaveUniqueItems.cs @@ -1,5 +1,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using System.Collections.Generic; @@ -18,28 +19,35 @@ public class CollectionShouldOnlyHaveUniqueItemsAnalyzer : FluentAssertionsAnaly protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true); - protected override Diagnostic AnalyzeExpressionStatement(ExpressionStatementSyntax statement) + protected override IEnumerable<(FluentAssertionsCSharpSyntaxVisitor, BecauseArgumentsSyntaxVisitor)> Visitors { - return null; - var visitor = new CollectionShouldOnlyHaveUniqueItemsSyntaxVisitor(); - statement.Accept(visitor); + get + { + yield return (new ShouldHaveSameCountThisCollectionDistinctSyntaxVisitor(), new BecauseArgumentsSyntaxVisitor("HaveSameCount", 1)); + } + } - if (visitor.IsValid) + private class ShouldHaveSameCountThisCollectionDistinctSyntaxVisitor : FluentAssertionsWithArgumentCSharpSyntaxVisitor + { + protected override string MethodContainingArgument => "HaveSameCount"; + public ShouldHaveSameCountThisCollectionDistinctSyntaxVisitor() : base("Should", "HaveSameCount") { - var properties = new Dictionary - { - [Constants.DiagnosticProperties.VariableName] = visitor.VariableName, - [Constants.DiagnosticProperties.Title] = Title - }.ToImmutableDictionary(); - throw new System.NotImplementedException(); + } + + protected override ExpressionSyntax ModifyArgument(ExpressionSyntax expression) + { + var visitor = new CollectionDistinctSyntaxVisitor(); + expression.Accept(visitor); + + return (visitor.IsValid && visitor.VariableName == VariableName) ? expression : null; + } - return Diagnostic.Create( - descriptor: Rule, - location: statement.Expression.GetLocation(), - properties: properties, - messageArgs: visitor.VariableName); + private class CollectionDistinctSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor + { + public CollectionDistinctSyntaxVisitor() : base("Distinct") + { + } } - return null; } } @@ -49,16 +57,6 @@ public class CollectionShouldOnlyHaveUniqueItemsCodeFix : FluentAssertionsCodeFi public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(CollectionShouldOnlyHaveUniqueItemsAnalyzer.DiagnosticId); protected override StatementSyntax GetNewStatement(FluentAssertionsDiagnosticProperties properties) - { - throw new System.NotImplementedException(); - } - } - - public class CollectionShouldOnlyHaveUniqueItemsSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor - { - public CollectionShouldOnlyHaveUniqueItemsSyntaxVisitor() : base("###") - { - throw new System.NotImplementedException(); - } + => SyntaxFactory.ParseStatement($"{properties.VariableName}.Should().OnlyHaveUniqueItems({properties.BecauseArgumentsString});"); } } diff --git a/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldOnlyHaveUniqueItemsByComparer.cs b/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldOnlyHaveUniqueItemsByComparer.cs index 87fcc0af..749e960b 100644 --- a/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldOnlyHaveUniqueItemsByComparer.cs +++ b/src/FluentAssertions.BestPractices/Tips/Collections/CollectionShouldOnlyHaveUniqueItemsByComparer.cs @@ -1,5 +1,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using System.Collections.Generic; @@ -14,32 +15,24 @@ public class CollectionShouldOnlyHaveUniqueItemsByComparerAnalyzer : FluentAsser public const string DiagnosticId = Constants.Tips.Collections.CollectionShouldOnlyHaveUniqueItemsByComparer; public const string Category = Constants.Tips.Category; - public const string Message = "Use {0} .Should() followed by ### instead."; + public const string Message = "Use {0} .Should() followed by .OnlyHaveUniqueItems() instead."; protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true); - protected override Diagnostic AnalyzeExpressionStatement(ExpressionStatementSyntax statement) + protected override IEnumerable<(FluentAssertionsCSharpSyntaxVisitor, BecauseArgumentsSyntaxVisitor)> Visitors { - return null; - var visitor = new CollectionShouldOnlyHaveUniqueItemsByComparerSyntaxVisitor(); - statement.Accept(visitor); - - if (visitor.IsValid) + get { - var properties = new Dictionary - { - [Constants.DiagnosticProperties.VariableName] = visitor.VariableName, - [Constants.DiagnosticProperties.Title] = Title - }.ToImmutableDictionary(); - throw new System.NotImplementedException(); + yield return (new SelectShouldOnlyHaveUniqueItemsSyntaxVisitor(), new BecauseArgumentsSyntaxVisitor("OnlyHaveUniqueItems", 0)); + } + } - return Diagnostic.Create( - descriptor: Rule, - location: statement.Expression.GetLocation(), - properties: properties, - messageArgs: visitor.VariableName); + private class SelectShouldOnlyHaveUniqueItemsSyntaxVisitor : FluentAssertionsWithLambdaArgumentCSharpSyntaxVisitor + { + protected override string MethodContainingLambda => "Select"; + public SelectShouldOnlyHaveUniqueItemsSyntaxVisitor() : base("Select", "Should", "OnlyHaveUniqueItems") + { } - return null; } } @@ -49,16 +42,6 @@ public class CollectionShouldOnlyHaveUniqueItemsByComparerCodeFix : FluentAssert public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(CollectionShouldOnlyHaveUniqueItemsByComparerAnalyzer.DiagnosticId); protected override StatementSyntax GetNewStatement(FluentAssertionsDiagnosticProperties properties) - { - throw new System.NotImplementedException(); - } - } - - public class CollectionShouldOnlyHaveUniqueItemsByComparerSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor - { - public CollectionShouldOnlyHaveUniqueItemsByComparerSyntaxVisitor() : base("###") - { - throw new System.NotImplementedException(); - } + => SyntaxFactory.ParseStatement($"{properties}.Should().OnlyHaveUniqueItems({properties.CombineWithBecauseArgumentsString(properties.LambdaString)})"); } } diff --git a/src/FluentAssertions.BestPractices/Utilities/FluentAssertionsWithArgumentsCSharpSyntaxVisitor.cs b/src/FluentAssertions.BestPractices/Utilities/FluentAssertionsWithArgumentsCSharpSyntaxVisitor.cs index d031c526..5566b806 100644 --- a/src/FluentAssertions.BestPractices/Utilities/FluentAssertionsWithArgumentsCSharpSyntaxVisitor.cs +++ b/src/FluentAssertions.BestPractices/Utilities/FluentAssertionsWithArgumentsCSharpSyntaxVisitor.cs @@ -7,13 +7,14 @@ namespace FluentAssertions.BestPractices public abstract class FluentAssertionsWithArgumentsCSharpSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor { protected readonly Dictionary<(string Method, int Index), ExpressionSyntax> Arguments = new Dictionary<(string Method, int Index), ExpressionSyntax>(); - protected abstract bool AreArgumentsValid { get; } - public override bool IsValid => base.IsValid && AreArgumentsValid; + public override bool IsValid => base.IsValid && AreArgumentsValid(); protected FluentAssertionsWithArgumentsCSharpSyntaxVisitor(params string[] requiredMethods) : base(requiredMethods) { } + protected abstract bool AreArgumentsValid(); + public override void VisitArgumentList(ArgumentListSyntax node) { VisitArguments(node.Arguments); diff --git a/src/FluentAssertions.BestPractices/Utilities/FluentAssertionsWithLambdaArgumentCSharpSyntaxVisitor.cs b/src/FluentAssertions.BestPractices/Utilities/FluentAssertionsWithLambdaArgumentCSharpSyntaxVisitor.cs index c796d4b5..1856abf9 100644 --- a/src/FluentAssertions.BestPractices/Utilities/FluentAssertionsWithLambdaArgumentCSharpSyntaxVisitor.cs +++ b/src/FluentAssertions.BestPractices/Utilities/FluentAssertionsWithLambdaArgumentCSharpSyntaxVisitor.cs @@ -6,12 +6,12 @@ namespace FluentAssertions.BestPractices public abstract class FluentAssertionsWithLambdaArgumentCSharpSyntaxVisitor : FluentAssertionsWithArgumentsCSharpSyntaxVisitor { - protected virtual SimpleLambdaExpressionSyntax Lambda => Arguments[(MethodContainingLambda, 0)] as SimpleLambdaExpressionSyntax; + public virtual SimpleLambdaExpressionSyntax Lambda => Arguments[(MethodContainingLambda, 0)] as SimpleLambdaExpressionSyntax; protected abstract string MethodContainingLambda { get; } public override bool IsValid => base.IsValid && Lambda != null; - protected override bool AreArgumentsValid => Arguments.TryGetValue((MethodContainingLambda, 0), out var lambda) && lambda is SimpleLambdaExpressionSyntax; + protected override bool AreArgumentsValid() => Arguments.TryGetValue((MethodContainingLambda, 0), out var lambda) && lambda is SimpleLambdaExpressionSyntax; protected FluentAssertionsWithLambdaArgumentCSharpSyntaxVisitor(params string[] requiredMethods) : base(requiredMethods) { @@ -20,4 +20,4 @@ protected FluentAssertionsWithLambdaArgumentCSharpSyntaxVisitor(params string[] public override ImmutableDictionary ToDiagnosticProperties() => base.ToDiagnosticProperties().Add(Constants.DiagnosticProperties.LambdaString, Lambda.ToFullString()); } -} +} \ No newline at end of file