Skip to content

Commit 2ad09e4

Browse files
authored
Migrate numerics to IOperation (#259)
1 parent b1b25ba commit 2ad09e4

16 files changed

+210
-374
lines changed

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

Lines changed: 29 additions & 29 deletions
Large diffs are not rendered by default.

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

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public class NumericTests
1111
[AssertionDiagnostic("actual.Should().BeGreaterThan(0{0});")]
1212
[AssertionDiagnostic("actual.Should().BeGreaterThan(0{0}).ToString();")]
1313
[Implemented]
14-
public void NumericShouldBePositive_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic<NumericShouldBePositiveAnalyzer>(assertion);
14+
public void NumericShouldBePositive_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion, DiagnosticMetadata.NumericShouldBePositive_ShouldBeGreaterThan);
1515

1616
[DataTestMethod]
1717
[AssertionCodeFix(
@@ -21,13 +21,13 @@ public class NumericTests
2121
oldAssertion: "actual.Should().BeGreaterThan(0{0}).ToString();",
2222
newAssertion: "actual.Should().BePositive({0}).ToString();")]
2323
[Implemented]
24-
public void NumericShouldBePositive_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<NumericShouldBePositiveCodeFix, NumericShouldBePositiveAnalyzer>(oldAssertion, newAssertion);
24+
public void NumericShouldBePositive_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<FluentAssertionsCodeFix, FluentAssertionsOperationAnalyzer>(oldAssertion, newAssertion);
2525

2626
[DataTestMethod]
2727
[AssertionDiagnostic("actual.Should().BeLessThan(0{0});")]
2828
[AssertionDiagnostic("actual.Should().BeLessThan(0{0}).ToString();")]
2929
[Implemented]
30-
public void NumericShouldBeNegative_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic<NumericShouldBeNegativeAnalyzer>(assertion);
30+
public void NumericShouldBeNegative_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion, DiagnosticMetadata.NumericShouldBeNegative_ShouldBeLessThan);
3131

3232
[DataTestMethod]
3333
[AssertionCodeFix(
@@ -37,17 +37,19 @@ public class NumericTests
3737
oldAssertion: "actual.Should().BeLessThan(0{0}).ToString();",
3838
newAssertion: "actual.Should().BeNegative({0}).ToString();")]
3939
[Implemented]
40-
public void NumericShouldBeNegative_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<NumericShouldBeNegativeCodeFix, NumericShouldBeNegativeAnalyzer>(oldAssertion, newAssertion);
40+
public void NumericShouldBeNegative_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<FluentAssertionsCodeFix, FluentAssertionsOperationAnalyzer>(oldAssertion, newAssertion);
4141

4242
[DataTestMethod]
4343
[AssertionDiagnostic("actual.Should().BeGreaterOrEqualTo(lower{0}).And.BeLessOrEqualTo(upper);")]
4444
[AssertionDiagnostic("actual.Should().BeGreaterOrEqualTo(lower).And.BeLessOrEqualTo(upper{0});")]
45-
[AssertionDiagnostic("actual.Should().BeGreaterOrEqualTo(lower{0}).And.BeLessOrEqualTo(upper{0});")]
45+
[Implemented]
46+
public void NumericShouldBeInRange_BeGreaterOrEqualToAndBeLessOrEqualTo_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion, DiagnosticMetadata.NumericShouldBeInRange_BeGreaterOrEqualToAndBeLessOrEqualTo);
47+
48+
[DataTestMethod]
4649
[AssertionDiagnostic("actual.Should().BeLessOrEqualTo(upper{0}).And.BeGreaterOrEqualTo(lower);")]
4750
[AssertionDiagnostic("actual.Should().BeLessOrEqualTo(upper).And.BeGreaterOrEqualTo(lower{0});")]
48-
[AssertionDiagnostic("actual.Should().BeLessOrEqualTo(upper{0}).And.BeGreaterOrEqualTo(lower{0});")]
4951
[Implemented]
50-
public void NumericShouldBeInRange_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic<NumericShouldBeInRangeAnalyzer>(assertion);
52+
public void NumericShouldBeInRange_BeLessOrEqualToAndBeGreaterOrEqualTo_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion, DiagnosticMetadata.NumericShouldBeInRange_BeLessOrEqualToAndBeGreaterOrEqualTo);
5153

5254
[DataTestMethod]
5355
[AssertionCodeFix(
@@ -62,33 +64,30 @@ public class NumericTests
6264
[AssertionCodeFix(
6365
oldAssertion: "actual.Should().BeLessOrEqualTo(upper).And.BeGreaterOrEqualTo(lower{0});",
6466
newAssertion: "actual.Should().BeInRange(lower, upper{0});")]
65-
[Implemented]
66-
public void NumericShouldBeInRange_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<NumericShouldBeInRangeCodeFix, NumericShouldBeInRangeAnalyzer>(oldAssertion, newAssertion);
67+
[NotImplemented]
68+
public void NumericShouldBeInRange_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<FluentAssertionsCodeFix, FluentAssertionsOperationAnalyzer>(oldAssertion, newAssertion);
6769

6870
[DataTestMethod]
6971
[AssertionDiagnostic("Math.Abs(expected - actual).Should().BeLessOrEqualTo(delta{0});")]
7072
[Implemented]
71-
public void NumericShouldBeApproximately_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic<NumericShouldBeApproximatelyAnalyzer>(assertion);
73+
public void NumericShouldBeApproximately_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion, DiagnosticMetadata.NumericShouldBeApproximately_MathAbsShouldBeLessOrEqualTo);
7274

7375
[DataTestMethod]
7476
[AssertionCodeFix(
7577
oldAssertion: "Math.Abs(expected - actual).Should().BeLessOrEqualTo(delta{0});",
7678
newAssertion: "actual.Should().BeApproximately(expected, delta{0});")]
7779
[Implemented]
78-
public void NumericShouldBeApproximately_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<NumericShouldBeApproximatelyCodeFix, NumericShouldBeApproximatelyAnalyzer>(oldAssertion, newAssertion);
80+
public void NumericShouldBeApproximately_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<FluentAssertionsCodeFix, FluentAssertionsOperationAnalyzer>(oldAssertion, newAssertion);
7981

80-
private void VerifyCSharpDiagnostic<TDiagnosticAnalyzer>(string sourceAssertion) where TDiagnosticAnalyzer : Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer, new()
82+
private void VerifyCSharpDiagnostic(string sourceAssertion, DiagnosticMetadata metadata)
8183
{
8284
var source = GenerateCode.DoubleAssertion(sourceAssertion);
8385

84-
var type = typeof(TDiagnosticAnalyzer);
85-
var diagnosticId = (string)type.GetField("DiagnosticId").GetValue(null);
86-
var message = (string)type.GetField("Message").GetValue(null);
87-
8886
DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(source, new DiagnosticResult
8987
{
90-
Id = diagnosticId,
91-
Message = message,
88+
Id = FluentAssertionsOperationAnalyzer.DiagnosticId,
89+
Message = metadata.Message,
90+
VisitorName = metadata.Name,
9291
Locations = new DiagnosticResultLocation[]
9392
{
9493
new DiagnosticResultLocation("Test0.cs", 10, 13)

src/FluentAssertions.Analyzers/Tips/DiagnosticMetadata.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,11 @@ private DiagnosticMetadata(string message, string helpLink, [CallerMemberName] s
5454
public static DiagnosticMetadata CollectionShouldOnlyHaveUniqueItems_ShouldHaveSameCountThisCollectionDistinct { get; } = new("Use .Should().OnlyHaveUniqueItems()", GetHelpLink("Collections-33"));
5555
public static DiagnosticMetadata CollectionShouldOnlyHaveUniqueItemsByComparer_SelectShouldOnlyHaveUniqueItems { get; } = new("Use .Should().OnlyHaveUniqueItems()", GetHelpLink("Collections-34"));
5656

57+
public static DiagnosticMetadata NumericShouldBePositive_ShouldBeGreaterThan { get; } = new("Use .Should().BePositive()", GetHelpLink("Numeric-1"));
58+
public static DiagnosticMetadata NumericShouldBeNegative_ShouldBeLessThan { get; } = new("Use .Should().BeNegative()", GetHelpLink("Numeric-2"));
59+
public static DiagnosticMetadata NumericShouldBeInRange_BeGreaterOrEqualToAndBeLessOrEqualTo { get; } = new("Use .Should().BeInRange()", string.Empty);
60+
public static DiagnosticMetadata NumericShouldBeInRange_BeLessOrEqualToAndBeGreaterOrEqualTo { get; } = new("Use .Should().BeInRange()", string.Empty);
61+
public static DiagnosticMetadata NumericShouldBeApproximately_MathAbsShouldBeLessOrEqualTo { get; } = new("Use .Should().BeApproximately()", string.Empty);
62+
5763
private static string GetHelpLink(string id) => $"https://fluentassertions.com/tips/#{id}";
5864
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace FluentAssertions.Analyzers;
66

7-
public partial class CollectionCodeFix
7+
public partial class FluentAssertionsCodeFix
88
{
99
private ExpressionSyntax GetNewExpressionForSelectShouldEqualOtherCollectionSelectSyntaxVisitor(ExpressionSyntax expression)
1010
{

src/FluentAssertions.Analyzers/Tips/CollectionCodeFix.CollectionShouldNotBeNullOrEmpty.cs renamed to src/FluentAssertions.Analyzers/Tips/FluentAssertionsCodeFix.CollectionShouldNotBeNullOrEmpty.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22

33
namespace FluentAssertions.Analyzers;
44

5-
public partial class CollectionCodeFix
5+
public partial class FluentAssertionsCodeFix
66
{
77
private ExpressionSyntax GetCombinedAssertions(ExpressionSyntax expression, string removeMethod, string renameMethod)
8+
=> GetCombinedAssertions(expression, removeMethod, renameMethod, "NotBeNullOrEmpty");
9+
10+
private ExpressionSyntax GetCombinedAssertions(ExpressionSyntax expression, string removeMethod, string renameMethod, string newMethod)
811
{
912
var remove = NodeReplacement.RemoveAndExtractArguments(removeMethod);
1013
var newExpression = GetNewExpression(expression, NodeReplacement.RemoveMethodBefore(removeMethod), remove);
1114

12-
return GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments(renameMethod, "NotBeNullOrEmpty", remove.Arguments));
15+
return GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments(renameMethod, newMethod, remove.Arguments));
1316
}
1417
}

src/FluentAssertions.Analyzers/Tips/CollectionCodeFix.cs renamed to src/FluentAssertions.Analyzers/Tips/FluentAssertionsCodeFix.cs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
using System.Composition;
33
using Microsoft.CodeAnalysis;
44
using Microsoft.CodeAnalysis.CodeFixes;
5+
using Microsoft.CodeAnalysis.CSharp;
56
using Microsoft.CodeAnalysis.CSharp.Syntax;
67

78
namespace FluentAssertions.Analyzers;
89

9-
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CollectionCodeFix)), Shared]
10-
public partial class CollectionCodeFix : FluentAssertionsCodeFixProvider
10+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(FluentAssertionsCodeFix)), Shared]
11+
public partial class FluentAssertionsCodeFix : FluentAssertionsCodeFixProvider
1112
{
1213
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(FluentAssertionsOperationAnalyzer.DiagnosticId);
1314

@@ -188,6 +189,54 @@ protected override ExpressionSyntax GetNewExpression(ExpressionSyntax expression
188189
return GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments("Be", "HaveElementAt", remove.Arguments));
189190
}
190191

192+
case nameof(DiagnosticMetadata.NumericShouldBePositive_ShouldBeGreaterThan):
193+
return GetNewExpression(expression, NodeReplacement.RenameAndRemoveFirstArgument("BeGreaterThan", "BePositive"));
194+
case nameof(DiagnosticMetadata.NumericShouldBeNegative_ShouldBeLessThan):
195+
return GetNewExpression(expression, NodeReplacement.RenameAndRemoveFirstArgument("BeLessThan", "BeNegative"));
196+
197+
case nameof(DiagnosticMetadata.NumericShouldBeInRange_BeGreaterOrEqualToAndBeLessOrEqualTo):
198+
{
199+
var removeLess = NodeReplacement.RemoveAndExtractArguments("BeLessOrEqualTo");
200+
var newExpression = GetNewExpression(expression, NodeReplacement.RemoveMethodBefore("BeLessOrEqualTo"), removeLess);
201+
202+
var renameGreater = NodeReplacement.RenameAndExtractArguments("BeGreaterOrEqualTo", "BeInRange");
203+
newExpression = GetNewExpression(newExpression, renameGreater);
204+
205+
var arguments = renameGreater.Arguments.InsertRange(1, removeLess.Arguments);
206+
207+
var result = GetNewExpression(newExpression, NodeReplacement.WithArguments("BeInRange", arguments));
208+
209+
return result;
210+
}
211+
case nameof(DiagnosticMetadata.NumericShouldBeInRange_BeLessOrEqualToAndBeGreaterOrEqualTo):
212+
{
213+
var removeGreater = NodeReplacement.RemoveAndExtractArguments("BeGreaterOrEqualTo");
214+
var newExpression = GetNewExpression(expression, NodeReplacement.RemoveMethodBefore("BeGreaterOrEqualTo"), removeGreater);
215+
216+
var renameLess = NodeReplacement.RenameAndExtractArguments("BeLessOrEqualTo", "BeInRange");
217+
newExpression = GetNewExpression(newExpression, renameLess);
218+
219+
var arguments = removeGreater.Arguments.InsertRange(1, renameLess.Arguments);
220+
221+
return GetNewExpression(newExpression, NodeReplacement.WithArguments("BeInRange", arguments));
222+
}
223+
case nameof(DiagnosticMetadata.NumericShouldBeApproximately_MathAbsShouldBeLessOrEqualTo):
224+
{
225+
var remove = NodeReplacement.RemoveAndExtractArguments("Abs");
226+
var newExpression = GetNewExpression(expression, remove);
227+
228+
var subtractExpression = (BinaryExpressionSyntax)remove.Arguments[0].Expression;
229+
230+
var actual = subtractExpression.Right as IdentifierNameSyntax;
231+
var expected = subtractExpression.Left;
232+
233+
newExpression = GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments("BeLessOrEqualTo", "BeApproximately", new SeparatedSyntaxList<ArgumentSyntax>().Add(SyntaxFactory.Argument(expected))));
234+
235+
newExpression = RenameIdentifier(newExpression, "Math", actual.Identifier.Text);
236+
237+
return newExpression;
238+
}
239+
191240
default: throw new System.InvalidOperationException($"Invalid visitor name - {properties.VisitorName}");
192241
};
193242
}

src/FluentAssertions.Analyzers/Tips/FluentAssertionsOperationAnalyzer.Utils.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections;
23
using System.Collections.Generic;
34
using System.Linq;
@@ -28,7 +29,7 @@ public FluentAssertionsMetadata(Compilation compilation)
2829
IReadonlyDictionaryOfT2 = compilation.GetTypeByMetadataName(typeof(IReadOnlyDictionary<,>).FullName);
2930

3031
Enumerable = compilation.GetTypeByMetadataName(typeof(Enumerable).FullName);
31-
32+
Math = compilation.GetTypeByMetadataName(typeof(Math).FullName);
3233
}
3334
public INamedTypeSymbol AssertionExtensions { get; }
3435
public INamedTypeSymbol ReferenceTypeAssertionsOfT2 { get; }
@@ -39,5 +40,6 @@ public FluentAssertionsMetadata(Compilation compilation)
3940
public INamedTypeSymbol BooleanAssertionsOfT1 { get; }
4041
public INamedTypeSymbol NumericAssertionsOfT2 { get; }
4142
public INamedTypeSymbol Enumerable { get; }
43+
public INamedTypeSymbol Math { get; }
4244
}
4345
}

0 commit comments

Comments
 (0)