Skip to content

Commit 9f69a5a

Browse files
authored
Merge pull request #16 from fluentassertions/feature/GH-13
refactored analyzers visitors algorithm fixed #11 fixed #10 fixed #13
2 parents 0a0c026 + 55c5ee8 commit 9f69a5a

File tree

45 files changed

+413
-633
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+413
-633
lines changed

src/FluentAssertions.Analyzers.Tests/TestAttributes.cs

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ public class NotImplementedAttribute : TestCategoryBaseAttribute
1616
[AttributeUsage(AttributeTargets.Method)]
1717
public class ImplementedAttribute : TestCategoryBaseAttribute
1818
{
19+
public string Reason { get; set; }
20+
1921
public override IList<string> TestCategories => new[] { "Completed" };
2022
}
2123

src/FluentAssertions.Analyzers.Tests/Tips/CollectionTests.cs

+20-6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ public class CollectionTests
88
{
99
public TestContext TestContext { get; set; }
1010

11+
[AssertionDataTestMethod]
12+
[AssertionDiagnostic("nestedList.Should().NotBeNull({0}).And.ContainSingle().Which.Should().NotBeEmpty();")]
13+
[AssertionDiagnostic("nestedList.Should().NotBeNull().And.ContainSingle().Which.Should().NotBeEmpty({0});")]
14+
[AssertionDiagnostic("nestedList.Should().NotBeNull().And.ContainSingle({0}).Which.Should().NotBeEmpty();")]
15+
[NotImplemented]
16+
public void NoDiagnostics(string assertion) => VerifyCSharpNoDiagnosticsCodeBlock(assertion);
17+
18+
1119
[AssertionDataTestMethod]
1220
[AssertionDiagnostic("actual.Any().Should().BeTrue({0});")]
1321
[AssertionDiagnostic("actual.AsEnumerable().Any().Should().BeTrue({0}).And.ToString();")]
@@ -375,6 +383,8 @@ public class CollectionTests
375383
[AssertionDiagnostic("actual.Should().NotBeEmpty().And.NotBeNull({0});")]
376384
[AssertionDiagnostic("actual.AsEnumerable().Should().NotBeNull().And.NotBeEmpty({0}).And.ToString();")]
377385
[AssertionDiagnostic("actual.AsEnumerable().Should().NotBeEmpty().And.NotBeNull({0}).And.ToString();")]
386+
[AssertionDiagnostic("actual.AsEnumerable().Should().NotBeNull().And.HaveCount(2).And.NotBeEmpty({0}).And.ToString();")]
387+
[AssertionDiagnostic("actual.AsEnumerable().Should().NotBeEmpty().And.HaveCount(2).And.NotBeNull({0}).And.ToString();")]
378388
[Implemented]
379389
public void CollectionShouldNotBeNullOrEmpty_TestAnalyzer(string assertion) => VerifyCSharpDiagnosticCodeBlock<CollectionShouldNotBeNullOrEmptyAnalyzer>(assertion);
380390

@@ -390,7 +400,7 @@ public class CollectionTests
390400
newAssertion: "actual.Should().NotBeNullOrEmpty({0});")]
391401
[AssertionCodeFix(
392402
oldAssertion: "actual.Should().NotBeEmpty({0}).And.NotBeNull();",
393-
newAssertion: "actual.Should().NotBeNullOrEmpty({0});")]
403+
newAssertion: "actual.Should().NotBeNullOrEmpty({0});")]
394404
[AssertionCodeFix(
395405
oldAssertion: "actual.AsEnumerable().Should().NotBeNull().And.HaveCount(2).And.NotBeEmpty({0}).And.ToString();",
396406
newAssertion: "actual.AsEnumerable().Should().NotBeNullOrEmpty({0}).And.HaveCount(2).And.ToString();")]
@@ -400,8 +410,6 @@ public class CollectionTests
400410
[Implemented]
401411
public void CollectionShouldNotBeNullOrEmpty_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock<CollectionShouldNotBeNullOrEmptyCodeFix, CollectionShouldNotBeNullOrEmptyAnalyzer>(oldAssertion, newAssertion);
402412

403-
public void CollectionShouldNotBeNullOrEmptyMultipleReasons_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock<CollectionShouldNotBeNullOrEmptyCodeFix, CollectionShouldNotBeNullOrEmptyAnalyzer>(oldAssertion, newAssertion);
404-
405413
[AssertionDataTestMethod]
406414
[AssertionDiagnostic("actual.ElementAt(k).Should().Be(expectedItem{0});")]
407415
[AssertionDiagnostic("actual.ElementAt(6).Should().Be(expectedItem{0});")]
@@ -604,7 +612,7 @@ public class CollectionTests
604612
newAssertion: "actual.AsEnumerable().Should().HaveElementAt(0, null{0}).And.ToString();")]
605613
[Ignore("What Should Happen?")]
606614
public void CollectionShouldHaveElementAt0Null_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFixCodeBlock<CollectionShouldHaveElementAt0NullCodeFix, CollectionShouldHaveElementAt0NullAnalyzer>(oldAssertion, newAssertion);
607-
615+
608616
private void VerifyCSharpDiagnosticCodeBlock<TDiagnosticAnalyzer>(string sourceAssertion) where TDiagnosticAnalyzer : Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer, new()
609617
{
610618
var source = GenerateCode.EnumerableCodeBlockAssertion(sourceAssertion);
@@ -616,7 +624,7 @@ public class CollectionTests
616624
DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(source, new DiagnosticResult
617625
{
618626
Id = diagnosticId,
619-
Message = string.Format(message, GenerateCode.ActualVariableName),
627+
Message = message,
620628
Locations = new DiagnosticResultLocation[]
621629
{
622630
new DiagnosticResultLocation("Test0.cs", 11,13)
@@ -636,7 +644,7 @@ public class CollectionTests
636644
DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(source, new DiagnosticResult
637645
{
638646
Id = diagnosticId,
639-
Message = string.Format(message, GenerateCode.ActualVariableName),
647+
Message = message,
640648
Locations = new DiagnosticResultLocation[]
641649
{
642650
new DiagnosticResultLocation("Test0.cs", 10,16)
@@ -664,5 +672,11 @@ private void VerifyCSharpFixExpressionBody<TCodeFixProvider, TDiagnosticAnalyzer
664672

665673
DiagnosticVerifier.VerifyCSharpFix<TCodeFixProvider, TDiagnosticAnalyzer>(oldSource, newSource);
666674
}
675+
676+
private void VerifyCSharpNoDiagnosticsCodeBlock(string assertion)
677+
{
678+
var source = GenerateCode.EnumerableCodeBlockAssertion(assertion);
679+
DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(source);
680+
}
667681
}
668682
}

src/FluentAssertions.Analyzers.Tests/Tips/SanityTests.cs

+24-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,17 @@ namespace FluentAssertions.Analyzers.Tests.Tips
66
public class SanityTests
77
{
88
[TestMethod]
9-
[NotImplemented(Reason = "https://github.com/fluentassertions/fluentassertions.analyzers/issues/10")]
9+
[Implemented(Reason = "https://github.com/fluentassertions/fluentassertions.analyzers/issues/11")]
10+
public void CountWithPredicate()
11+
{
12+
const string assertion = "actual.Count(d => d.Message.Contains(\"a\")).Should().Be(2);";
13+
var source = GenerateCode.EnumerableCodeBlockAssertion(assertion);
14+
15+
DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(source);
16+
}
17+
18+
[TestMethod]
19+
[Implemented(Reason = "https://github.com/fluentassertions/fluentassertions.analyzers/issues/10")]
1020
public void AssertionCallMultipleMethodWithTheSameNameAndArguments()
1121
{
1222
const string assertion = "actual.Should().Contain(d => d.Message.Contains(\"a\")).And.Contain(d => d.Message.Contains(\"c\"));";
@@ -16,7 +26,7 @@ public void AssertionCallMultipleMethodWithTheSameNameAndArguments()
1626
}
1727

1828
[TestMethod]
19-
[NotImplemented(Reason = "https://github.com/fluentassertions/fluentassertions.analyzers/issues/13")]
29+
[Implemented(Reason = "https://github.com/fluentassertions/fluentassertions.analyzers/issues/13")]
2030
public void PropertyOfIndexerShouldBe_ShouldNotThrowException()
2131
{
2232
const string assertion = "actual[0].Message.Should().Be(\"test\");";
@@ -26,13 +36,24 @@ public void PropertyOfIndexerShouldBe_ShouldNotThrowException()
2636
}
2737

2838
[TestMethod]
29-
[NotImplemented(Reason = "https://github.com/fluentassertions/fluentassertions.analyzers/issues/13")]
39+
[Implemented(Reason = "https://github.com/fluentassertions/fluentassertions.analyzers/issues/13")]
3040
public void PropertyOfElementAtShouldBe_ShouldNotTriggerDiagnostic()
3141
{
3242
const string assertion = "actual.ElementAt(0).Message.Should().Be(\"test\");";
3343
var source = GenerateCode.EnumerableCodeBlockAssertion(assertion);
3444

3545
DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(source);
3646
}
47+
48+
[TestMethod]
49+
[Implemented(Reason = "https://github.com/fluentassertions/fluentassertions.analyzers/issues/10")]
50+
public void NestedAssertions_ShouldNotTrigger()
51+
{
52+
const string declaration = "var nestedList = new List<List<int>>();";
53+
const string assertion = "nestedList.Should().NotBeNull().And.ContainSingle().Which.Should().NotBeEmpty();";
54+
var source = GenerateCode.EnumerableCodeBlockAssertion(declaration + assertion);
55+
56+
DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(source);
57+
}
3758
}
3859
}

src/FluentAssertions.Analyzers/Tips/Collections/CollectionShouldBeEmpty.cs

+9-16
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class CollectionShouldBeEmptyAnalyzer : FluentAssertionsAnalyzer
1515
public const string DiagnosticId = Constants.Tips.Collections.CollectionsShouldBeEmpty;
1616
public const string Category = Constants.Tips.Category;
1717

18-
public const string Message = "Use {0} .Should() followed by .BeEmpty() instead.";
18+
public const string Message = "Use .Should().BeEmpty() instead.";
1919

2020
protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true);
2121
protected override IEnumerable<FluentAssertionsCSharpSyntaxVisitor> Visitors
@@ -27,31 +27,24 @@ protected override IEnumerable<FluentAssertionsCSharpSyntaxVisitor> Visitors
2727
}
2828
}
2929

30-
public class AnyShouldBeFalseSyntaxVisitor : FluentAssertionsWithoutLambdaArgumentCSharpSyntaxVisitor
30+
public class AnyShouldBeFalseSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor
3131
{
32-
protected override string MathodNotContainingLambda => "Any";
33-
34-
public AnyShouldBeFalseSyntaxVisitor() : base("Any", "Should", "BeFalse")
32+
public AnyShouldBeFalseSyntaxVisitor() : base(MemberValidator.MathodNotContainingLambda("Any"), MemberValidator.Should, new MemberValidator("BeFalse"))
3533
{
3634
}
3735
}
36+
3837
public class ShouldHaveCount0SyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor
3938
{
40-
private bool _haveCountMethodHas0Argument;
41-
42-
public override bool IsValid => base.IsValid && _haveCountMethodHas0Argument;
43-
44-
public ShouldHaveCount0SyntaxVisitor() : base("Should", "HaveCount")
39+
public ShouldHaveCount0SyntaxVisitor() : base(MemberValidator.Should, new MemberValidator("HaveCount", HaveCountArgumentsValidator))
4540
{
4641
}
4742

48-
public override void VisitArgumentList(ArgumentListSyntax node)
43+
private static bool HaveCountArgumentsValidator(SeparatedSyntaxList<ArgumentSyntax> arguments)
4944
{
50-
if (!node.Arguments.Any()) return;
51-
if (CurrentMethod != "HaveCount") return;
45+
if (!arguments.Any()) return false;
5246

53-
_haveCountMethodHas0Argument =
54-
node.Arguments[0].Expression is LiteralExpressionSyntax literal
47+
return arguments[0].Expression is LiteralExpressionSyntax literal
5548
&& literal.Token.Value is int argument
5649
&& argument == 0;
5750
}
@@ -62,7 +55,7 @@ node.Arguments[0].Expression is LiteralExpressionSyntax literal
6255
public class CollectionShouldBeEmptyCodeFix : FluentAssertionsCodeFixProvider
6356
{
6457
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(CollectionShouldBeEmptyAnalyzer.DiagnosticId);
65-
58+
6659
protected override ExpressionSyntax GetNewExpression(ExpressionSyntax expression, FluentAssertionsDiagnosticProperties properties)
6760
{
6861
switch (properties.VisitorName)

src/FluentAssertions.Analyzers/Tips/Collections/CollectionShouldBeInAscendingOrder.cs

+5-22
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class CollectionShouldBeInAscendingOrderAnalyzer : FluentAssertionsAnalyz
1414
public const string DiagnosticId = Constants.Tips.Collections.CollectionShouldBeInAscendingOrder;
1515
public const string Category = Constants.Tips.Category;
1616

17-
public const string Message = "Use {0} .Should() followed by .BeInAscendingOrder() instead.";
17+
public const string Message = "Use .Should().BeInAscendingOrder() instead.";
1818

1919
protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true);
2020
protected override IEnumerable<FluentAssertionsCSharpSyntaxVisitor> Visitors
@@ -24,28 +24,11 @@ protected override IEnumerable<FluentAssertionsCSharpSyntaxVisitor> Visitors
2424
yield return new OrderByShouldEqualSyntaxVisitor();
2525
}
2626
}
27-
private class OrderByShouldEqualSyntaxVisitor : FluentAssertionsWithLambdaArgumentCSharpSyntaxVisitor
28-
{
29-
private bool _argumentIsSelf;
30-
protected override string MethodContainingLambda => "OrderBy";
31-
32-
public override bool IsValid => base.IsValid && _argumentIsSelf;
33-
34-
public OrderByShouldEqualSyntaxVisitor() : base("OrderBy", "Should", "Equal")
35-
{
36-
}
3727

38-
public override void VisitArgumentList(ArgumentListSyntax node)
28+
private class OrderByShouldEqualSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor
29+
{
30+
public OrderByShouldEqualSyntaxVisitor() : base(MemberValidator.MathodContainingLambda("OrderBy"), MemberValidator.Should, MemberValidator.ArgumentIsVariable("Equal"))
3931
{
40-
if (!node.Arguments.Any()) return;
41-
if (CurrentMethod != "Equal")
42-
{
43-
base.VisitArgumentList(node);
44-
return;
45-
}
46-
47-
_argumentIsSelf = node.Arguments[0].Expression is IdentifierNameSyntax identifier
48-
&& identifier.Identifier.Text == VariableName;
4932
}
5033
}
5134
}
@@ -54,7 +37,7 @@ public override void VisitArgumentList(ArgumentListSyntax node)
5437
public class CollectionShouldBeInAscendingOrderCodeFix : FluentAssertionsCodeFixProvider
5538
{
5639
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(CollectionShouldBeInAscendingOrderAnalyzer.DiagnosticId);
57-
40+
5841
protected override ExpressionSyntax GetNewExpression(ExpressionSyntax expression, FluentAssertionsDiagnosticProperties properties)
5942
{
6043
var remove = NodeReplacement.RemoveAndExtractArguments("OrderBy");

src/FluentAssertions.Analyzers/Tips/Collections/CollectionShouldBeInDescendingOrder.cs

+4-21
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class CollectionShouldBeInDescendingOrderAnalyzer : FluentAssertionsAnaly
1414
public const string DiagnosticId = Constants.Tips.Collections.CollectionShouldBeInDescendingOrder;
1515
public const string Category = Constants.Tips.Category;
1616

17-
public const string Message = "Use {0} .Should() followed by .BeInDescendingOrder() instead.";
17+
public const string Message = "Use .Should().BeInDescendingOrder() instead.";
1818

1919
protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true);
2020
protected override IEnumerable<FluentAssertionsCSharpSyntaxVisitor> Visitors
@@ -24,28 +24,11 @@ protected override IEnumerable<FluentAssertionsCSharpSyntaxVisitor> Visitors
2424
yield return new OrderByDescendingShouldEqualSyntaxVisitor();
2525
}
2626
}
27-
private class OrderByDescendingShouldEqualSyntaxVisitor : FluentAssertionsWithLambdaArgumentCSharpSyntaxVisitor
28-
{
29-
private bool _argumentIsSelf;
30-
protected override string MethodContainingLambda => "OrderByDescending";
31-
32-
public override bool IsValid => base.IsValid && _argumentIsSelf;
3327

34-
public OrderByDescendingShouldEqualSyntaxVisitor() : base("OrderByDescending", "Should", "Equal")
35-
{
36-
}
37-
38-
public override void VisitArgumentList(ArgumentListSyntax node)
28+
private class OrderByDescendingShouldEqualSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor
29+
{
30+
public OrderByDescendingShouldEqualSyntaxVisitor() : base(MemberValidator.MathodContainingLambda("OrderByDescending"), MemberValidator.Should, MemberValidator.ArgumentIsVariable("Equal"))
3931
{
40-
if (!node.Arguments.Any()) return;
41-
if (CurrentMethod != "Equal")
42-
{
43-
base.VisitArgumentList(node);
44-
return;
45-
}
46-
47-
_argumentIsSelf = node.Arguments[0].Expression is IdentifierNameSyntax identifier
48-
&& identifier.Identifier.Text == VariableName;
4932
}
5033
}
5134
}

src/FluentAssertions.Analyzers/Tips/Collections/CollectionShouldContainItem.cs

+2-16
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class CollectionShouldContainItemAnalyzer : FluentAssertionsAnalyzer
1414
public const string DiagnosticId = Constants.Tips.Collections.CollectionShouldContainItem;
1515
public const string Category = Constants.Tips.Category;
1616

17-
public const string Message = "Use {0} .Should() followed by Contain() instead.";
17+
public const string Message = "Use .Should()Contain() instead.";
1818

1919
protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true);
2020

@@ -28,22 +28,8 @@ protected override IEnumerable<FluentAssertionsCSharpSyntaxVisitor> Visitors
2828

2929
private class ContainsShouldBeTrueSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor
3030
{
31-
public string ExpectedItemString { get; private set; }
32-
public override bool IsValid => base.IsValid && ExpectedItemString != null;
33-
34-
public ContainsShouldBeTrueSyntaxVisitor() : base("Contains", "Should", "BeTrue")
35-
{
36-
}
37-
38-
public override ImmutableDictionary<string, string> ToDiagnosticProperties()
39-
=> base.ToDiagnosticProperties().Add(Constants.DiagnosticProperties.ExpectedItemString, ExpectedItemString);
40-
41-
public override void VisitArgumentList(ArgumentListSyntax node)
31+
public ContainsShouldBeTrueSyntaxVisitor() : base(new MemberValidator("Contains"), MemberValidator.Should, new MemberValidator("BeTrue"))
4232
{
43-
if (!node.Arguments.Any()) return;
44-
if (CurrentMethod != "Contains") return;
45-
46-
ExpectedItemString = node.Arguments[0].ToFullString();
4733
}
4834
}
4935
}

src/FluentAssertions.Analyzers/Tips/Collections/CollectionShouldContainProperty.cs

+6-7
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class CollectionShouldContainPropertyAnalyzer : FluentAssertionsAnalyzer
1414
public const string DiagnosticId = Constants.Tips.Collections.CollectionShouldContainProperty;
1515
public const string Category = Constants.Tips.Category;
1616

17-
public const string Message = "Use {0} .Should() followed by .Contain() instead.";
17+
public const string Message = "Use .Should().Contain() instead.";
1818

1919
protected override DiagnosticDescriptor Rule => new DiagnosticDescriptor(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true);
2020
protected override IEnumerable<FluentAssertionsCSharpSyntaxVisitor> Visitors
@@ -26,17 +26,16 @@ protected override IEnumerable<FluentAssertionsCSharpSyntaxVisitor> Visitors
2626
}
2727
}
2828

29-
public class AnyShouldBeTrueSyntaxVisitor : FluentAssertionsWithLambdaArgumentCSharpSyntaxVisitor
29+
public class AnyShouldBeTrueSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor
3030
{
31-
protected override string MethodContainingLambda => "Any";
32-
public AnyShouldBeTrueSyntaxVisitor() : base("Any", "Should", "BeTrue")
31+
public AnyShouldBeTrueSyntaxVisitor() : base(MemberValidator.MathodContainingLambda("Any"), MemberValidator.Should, new MemberValidator("BeTrue"))
3332
{
3433
}
3534
}
36-
public class WhereShouldNotBeEmptySyntaxVisitor : FluentAssertionsWithLambdaArgumentCSharpSyntaxVisitor
35+
36+
public class WhereShouldNotBeEmptySyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor
3737
{
38-
protected override string MethodContainingLambda => "Where";
39-
public WhereShouldNotBeEmptySyntaxVisitor() : base("Where", "Should", "NotBeEmpty")
38+
public WhereShouldNotBeEmptySyntaxVisitor() : base(MemberValidator.MathodContainingLambda("Where"), MemberValidator.Should, new MemberValidator("NotBeEmpty"))
4039
{
4140
}
4241
}

0 commit comments

Comments
 (0)