Skip to content

Commit

Permalink
Refactor collection intersection code
Browse files Browse the repository at this point in the history
  • Loading branch information
Meir017 committed Dec 10, 2023
1 parent 653d4b0 commit 1eb392a
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 126 deletions.
8 changes: 4 additions & 4 deletions src/FluentAssertions.Analyzers.Tests/Tips/CollectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ public void CollectionShouldContainSingle_TestAnalyzer_GenericIEnumerableShouldR
[AssertionDiagnostic("actual.AsEnumerable().Skip(k).First().Should().Be(expectedItem{0}).And.ToString();")]
[AssertionDiagnostic("actual.AsEnumerable().Skip(6).First().Should().Be(expectedItem{0}).And.ToString();")]
[Implemented]
public void CollectionShouldHaveElementAt_TestAnalyzer(string assertion) => VerifyCSharpDiagnosticCodeBlock<CollectionShouldHaveElementAtAnalyzer>(assertion);
public void CollectionShouldHaveElementAt_TestAnalyzer(string assertion) => VerifyCSharpDiagnosticCodeBlock<CollectionAnalyzer>(assertion);

[AssertionDataTestMethod]
[AssertionCodeFix(
Expand Down Expand Up @@ -507,7 +507,7 @@ public void CollectionShouldContainSingle_TestAnalyzer_GenericIEnumerableShouldR
oldAssertion: "actual.AsEnumerable().Skip(6).First().Should().Be(expectedItem{0}).And.ToString();",
newAssertion: "actual.AsEnumerable().Should().HaveElementAt(6, expectedItem{0}).And.ToString();")]
[Implemented]
public void CollectionShouldHaveElementAt_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock<CollectionShouldHaveElementAtCodeFix, CollectionShouldHaveElementAtAnalyzer>(oldAssertion, newAssertion);
public void CollectionShouldHaveElementAt_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock<CollectionCodeFix, CollectionAnalyzer>(oldAssertion, newAssertion);

[AssertionDataTestMethod]
[AssertionDiagnostic("actual.OrderBy(x => x.BooleanProperty).Should().Equal(actual{0});")]
Expand Down Expand Up @@ -577,7 +577,7 @@ public void CollectionShouldContainSingle_TestAnalyzer_GenericIEnumerableShouldR
[AssertionDiagnostic("actual.Intersect(expected).Should().NotBeEmpty({0});")]
[AssertionDiagnostic("actual.AsEnumerable().Intersect(expected).Should().NotBeEmpty({0}).And.ToString();")]
[Implemented]
public void CollectionShouldIntersectWith_TestAnalyzer(string assertion) => VerifyCSharpDiagnosticCodeBlock<CollectionShouldIntersectWithAnalyzer>(assertion);
public void CollectionShouldIntersectWith_TestAnalyzer(string assertion) => VerifyCSharpDiagnosticCodeBlock<CollectionAnalyzer>(assertion);

[AssertionDataTestMethod]
[AssertionCodeFix(
Expand All @@ -587,7 +587,7 @@ public void CollectionShouldContainSingle_TestAnalyzer_GenericIEnumerableShouldR
oldAssertion: "actual.AsEnumerable().Intersect(expected).Should().NotBeEmpty({0}).And.ToString();",
newAssertion: "actual.AsEnumerable().Should().IntersectWith(expected{0}).And.ToString();")]
[Implemented]
public void CollectionShouldIntersectWith_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock<CollectionShouldIntersectWithCodeFix, CollectionShouldIntersectWithAnalyzer>(oldAssertion, newAssertion);
public void CollectionShouldIntersectWith_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock<CollectionCodeFix, CollectionAnalyzer>(oldAssertion, newAssertion);

[AssertionDataTestMethod]
[AssertionDiagnostic("actual.Select(x => x.BooleanProperty).Should().NotContainNulls({0});")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,11 @@ protected override IEnumerable<FluentAssertionsCSharpSyntaxVisitor> Visitors
yield return new CollectionShouldHaveCountLessOrEqualTo.CountShouldBeLessOrEqualToSyntaxVisitor();
yield return new CollectionShouldHaveCountLessThan.CountShouldBeLessThanSyntaxVisitor();

// TODO: Add support for CollectionShouldHaveElementAt
yield return new CollectionShouldHaveElementAt.ElementAtIndexShouldBeSyntaxVisitor();
yield return new CollectionShouldHaveElementAt.IndexerShouldBeSyntaxVisitor();
yield return new CollectionShouldHaveElementAt.SkipFirstShouldBeSyntaxVisitor();

yield return new CollectionShouldIntersectWith.IntersectShouldNotBeEmptySyntaxVisitor();

yield return new CollectionShouldHaveSameCount.ShouldHaveCountOtherCollectionCountSyntaxVisitor();

Expand Down Expand Up @@ -153,10 +157,8 @@ protected override ExpressionSyntax GetNewExpression(ExpressionSyntax expression

return GetNewExpression(newExpression, NodeReplacement.PrependArguments("ContainSingle", remove.Arguments));
}

case nameof(CollectionShouldEqualOtherCollectionByComparer.SelectShouldEqualOtherCollectionSelectSyntaxVisitor):
return GetNewExpressionForSelectShouldEqualOtherCollectionSelectSyntaxVisitor(expression);

case nameof(CollectionShouldHaveCount.CountShouldBe0SyntaxVisitor):
return GetNewExpression(expression, NodeReplacement.Remove("Count"), NodeReplacement.RenameAndRemoveFirstArgument("Be", "BeEmpty"));
case nameof(CollectionShouldHaveCount.CountShouldBe1SyntaxVisitor):
Expand All @@ -173,9 +175,36 @@ protected override ExpressionSyntax GetNewExpression(ExpressionSyntax expression
return GetNewExpression(expression, NodeReplacement.Remove("Count"), NodeReplacement.Rename("BeLessOrEqualTo", "HaveCountLessOrEqualTo"));
case nameof(CollectionShouldHaveCountLessThan.CountShouldBeLessThanSyntaxVisitor):
return GetNewExpression(expression, NodeReplacement.Remove("Count"), NodeReplacement.Rename("BeLessThan", "HaveCountLessThan"));
case nameof(CollectionShouldHaveElementAt.ElementAtIndexShouldBeSyntaxVisitor):
{
var remove = NodeReplacement.RemoveAndExtractArguments("ElementAt");
var newExpression = GetNewExpression(expression, remove);

return GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments("Be", "HaveElementAt", remove.Arguments));
}
case nameof(CollectionShouldHaveElementAt.IndexerShouldBeSyntaxVisitor):
{
var remove = NodeReplacement.RemoveAndRetrieveIndexerArguments("Should");
var newExpression = GetNewExpression(expression, remove);

return GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments("Be", "HaveElementAt", remove.Arguments));
}
case nameof(CollectionShouldHaveElementAt.SkipFirstShouldBeSyntaxVisitor):
{
var remove = NodeReplacement.RemoveAndExtractArguments("Skip");
var newExpression = GetNewExpression(expression, remove, NodeReplacement.Remove("First"));

return GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments("Be", "HaveElementAt", remove.Arguments));
}
case nameof(CollectionShouldIntersectWith.IntersectShouldNotBeEmptySyntaxVisitor):
{
var remove = NodeReplacement.RemoveAndExtractArguments("Intersect");
var newExpression = GetNewExpression(expression, remove);

return GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments("NotBeEmpty", "IntersectWith", remove.Arguments));
}
case nameof(CollectionShouldHaveSameCount.ShouldHaveCountOtherCollectionCountSyntaxVisitor):
return GetNewExpression(expression, NodeReplacement.RenameAndRemoveInvocationOfMethodOnFirstArgument("HaveCount", "HaveSameCount"));

case nameof(CollectionShouldNotContainItem.ContainsShouldBeFalseSyntaxVisitor):
{
var remove = NodeReplacement.RemoveAndExtractArguments("Contains");
Expand All @@ -190,7 +219,6 @@ protected override ExpressionSyntax GetNewExpression(ExpressionSyntax expression

return GetNewExpression(newExpression, NodeReplacement.PrependArguments("NotContainNulls", remove.Arguments));
}

case nameof(CollectionShouldNotContainProperty.AnyLambdaShouldBeFalseSyntaxVisitor):
{
var remove = NodeReplacement.RemoveAndExtractArguments("Any");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,45 +1,7 @@
using FluentAssertions.Analyzers.Utilities;
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;

namespace FluentAssertions.Analyzers;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class CollectionShouldHaveElementAtAnalyzer : CollectionAnalyzer
public class CollectionShouldHaveElementAt
{
public const string DiagnosticId = Constants.Tips.Collections.CollectionShouldHaveElementAt;
public const string Category = Constants.Tips.Category;

public const string Message = "Use .Should().HaveElementAt() instead.";

protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true);
protected override IEnumerable<FluentAssertionsCSharpSyntaxVisitor> Visitors
{
get
{
yield return new ElementAtIndexShouldBeSyntaxVisitor();
yield return new IndexerShouldBeSyntaxVisitor();
yield return new SkipFirstShouldBeSyntaxVisitor();
}
}

protected override bool ShouldAnalyzeVariableNamedType(INamedTypeSymbol type, SemanticModel semanticModel)
{
var iReadOnlyDictionaryType = semanticModel.GetIReadOnlyDictionaryType();
var iDictionaryType = semanticModel.GetGenericIDictionaryType();
if (type.IsTypeOrConstructedFromTypeOrImplementsType(iReadOnlyDictionaryType)
|| type.IsTypeOrConstructedFromTypeOrImplementsType(iDictionaryType))
{
return false;
}

return base.ShouldAnalyzeVariableNamedType(type, semanticModel);
}

public class ElementAtIndexShouldBeSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor
{
Expand All @@ -61,36 +23,4 @@ public class SkipFirstShouldBeSyntaxVisitor : FluentAssertionsCSharpSyntaxVisito
{
}
}
}

[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CollectionShouldHaveElementAtCodeFix)), Shared]
public class CollectionShouldHaveElementAtCodeFix : FluentAssertionsCodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(CollectionShouldHaveElementAtAnalyzer.DiagnosticId);

protected override ExpressionSyntax GetNewExpression(ExpressionSyntax expression, FluentAssertionsDiagnosticProperties properties)
{
if (properties.VisitorName == nameof(CollectionShouldHaveElementAtAnalyzer.ElementAtIndexShouldBeSyntaxVisitor))
{
var remove = NodeReplacement.RemoveAndExtractArguments("ElementAt");
var newExpression = GetNewExpression(expression, remove);

return GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments("Be", "HaveElementAt", remove.Arguments));
}
else if (properties.VisitorName == nameof(CollectionShouldHaveElementAtAnalyzer.IndexerShouldBeSyntaxVisitor))
{
var remove = NodeReplacement.RemoveAndRetrieveIndexerArguments("Should");
var newExpression = GetNewExpression(expression, remove);

return GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments("Be", "HaveElementAt", remove.Arguments));
}
else if (properties.VisitorName == nameof(CollectionShouldHaveElementAtAnalyzer.SkipFirstShouldBeSyntaxVisitor))
{
var remove = NodeReplacement.RemoveAndExtractArguments("Skip");
var newExpression = GetNewExpression(expression, remove, NodeReplacement.Remove("First"));

return GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments("Be", "HaveElementAt", remove.Arguments));
}
throw new System.InvalidOperationException($"Invalid visitor name - {properties.VisitorName}");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,48 +1,11 @@
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;

namespace FluentAssertions.Analyzers;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class CollectionShouldIntersectWithAnalyzer : CollectionAnalyzer
public static class CollectionShouldIntersectWith
{
public const string DiagnosticId = Constants.Tips.Collections.CollectionShouldIntersectWith;
public const string Category = Constants.Tips.Category;

public const string Message = "Use .Should().IntersectWith() instead.";

protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true);
protected override IEnumerable<FluentAssertionsCSharpSyntaxVisitor> Visitors
{
get
{
yield return new IntersectShouldNotBeEmptySyntaxVisitor();
}
}

public class IntersectShouldNotBeEmptySyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor
{
public IntersectShouldNotBeEmptySyntaxVisitor() : base(MemberValidator.HasArguments("Intersect"), MemberValidator.Should, new MemberValidator("NotBeEmpty"))
{
}
}
}

[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CollectionShouldIntersectWithCodeFix)), Shared]
public class CollectionShouldIntersectWithCodeFix : FluentAssertionsCodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(CollectionShouldIntersectWithAnalyzer.DiagnosticId);

protected override ExpressionSyntax GetNewExpression(ExpressionSyntax expression, FluentAssertionsDiagnosticProperties properties)
{
var remove = NodeReplacement.RemoveAndExtractArguments("Intersect");
var newExpression = GetNewExpression(expression, remove);

return GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments("NotBeEmpty", "IntersectWith", remove.Arguments));
}
}
}
8 changes: 4 additions & 4 deletions src/FluentAssertions.Analyzers/Utilities/HelpLinks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ static HelpLinks()
[typeof(CollectionShouldNotContainProperty.ShouldOnlyContainSyntaxVisitor)] = GetHelpLink("Collections-22"),
[typeof(CollectionShouldNotBeNullOrEmptyAnalyzer.ShouldNotBeNullAndNotBeEmptySyntaxVisitor)] = GetHelpLink("Collections-23"),
[typeof(CollectionShouldNotBeNullOrEmptyAnalyzer.ShouldNotBeEmptyAndNotBeNullSyntaxVisitor)] = GetHelpLink("Collections-23"),
[typeof(CollectionShouldHaveElementAtAnalyzer.ElementAtIndexShouldBeSyntaxVisitor)] = GetHelpLink("Collections-24"),
[typeof(CollectionShouldHaveElementAtAnalyzer.IndexerShouldBeSyntaxVisitor)] = GetHelpLink("Collections-25"),
[typeof(CollectionShouldHaveElementAtAnalyzer.SkipFirstShouldBeSyntaxVisitor)] = GetHelpLink("Collections-26"),
[typeof(CollectionShouldHaveElementAt.ElementAtIndexShouldBeSyntaxVisitor)] = GetHelpLink("Collections-24"),
[typeof(CollectionShouldHaveElementAt.IndexerShouldBeSyntaxVisitor)] = GetHelpLink("Collections-25"),
[typeof(CollectionShouldHaveElementAt.SkipFirstShouldBeSyntaxVisitor)] = GetHelpLink("Collections-26"),
[typeof(CollectionShouldBeInAscendingOrder.OrderByShouldEqualSyntaxVisitor)] = GetHelpLink("Collections-27"),
[typeof(CollectionShouldBeInDescendingOrder.OrderByDescendingShouldEqualSyntaxVisitor)] = GetHelpLink("Collections-28"),
[typeof(CollectionShouldEqualOtherCollectionByComparer.SelectShouldEqualOtherCollectionSelectSyntaxVisitor)] = GetHelpLink("Collections-29"),
[typeof(CollectionShouldNotIntersectWith.IntersectShouldBeEmptySyntaxVisitor)] = GetHelpLink("Collections-30"),
[typeof(CollectionShouldIntersectWithAnalyzer.IntersectShouldNotBeEmptySyntaxVisitor)] = GetHelpLink("Collections-31"),
[typeof(CollectionShouldIntersectWith.IntersectShouldNotBeEmptySyntaxVisitor)] = GetHelpLink("Collections-31"),
[typeof(CollectionShouldNotContainNulls.SelectShouldNotContainNullsSyntaxVisitor)] = GetHelpLink("Collections-32"),
[typeof(CollectionShouldOnlyHaveUniqueItems.ShouldHaveSameCountThisCollectionDistinctSyntaxVisitor)] = GetHelpLink("Collections-33"),
[typeof(CollectionShouldOnlyHaveUniqueItemsByComparer.SelectShouldOnlyHaveUniqueItemsSyntaxVisitor)] = GetHelpLink("Collections-34"),
Expand Down

0 comments on commit 1eb392a

Please sign in to comment.