Skip to content

Commit

Permalink
Custom message for IsPositiveInfinty and IsNegativeInfinity
Browse files Browse the repository at this point in the history
  • Loading branch information
antonioaversa committed Jan 25, 2023
1 parent ed5aced commit b2817b2
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public sealed class EqualityOnFloatingPoint : SonarDiagnosticAnalyzer
private static readonly Dictionary<string, string> SpecialMembers = new()
{
{ nameof(double.NaN), nameof(double.IsNaN) },
{ nameof(double.PositiveInfinity), nameof(double.IsPositiveInfinity) },
{ nameof(double.NegativeInfinity), nameof(double.IsNegativeInfinity) },
};

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule);
Expand Down Expand Up @@ -83,6 +85,11 @@ private static void CheckEquality(SonarSyntaxNodeReportingContext context)
}
}

// Only checks for type and member accessed. No need to check that proposedMethod exists in type because:
// - both special members and corresponding `IsXXX()` methods exist in double and float since .NET 1.1
// - both special members and corresponding `IsXXX()` methods exist in Half since its introduction, in .NET 5
// - while NFloat has been introduced in .NET 6, special members and corresponding IsXXX() methods have been introduced at the same time, in .NET 7
// - no other floating point types exist before .NET 7, and any new type after has to conform to `IFloatingPointIeee754`
private static string ProposedMessageForMemberAccess(SonarSyntaxNodeReportingContext context, ExpressionSyntax expression) =>
expression is MemberAccessExpressionSyntax memberAccess
&& SpecialMembers.TryGetValue(memberAccess.GetName(), out var proposedMethod)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,31 @@
using System.Runtime.InteropServices;

public class EqualityOnFloatingPoint
{
bool HalfEqual(Half first, Half second)
=> first == second; // Noncompliant {{Do not check floating point equality with exact values, use a range instead.}}
// ^^

bool NFloatEqual(NFloat first, NFloat second)
=> first == second; // Noncompliant

bool IsEpsilon<T>(T value) where T : IFloatingPointIeee754<T>
=> value == T.Epsilon; // Noncompliant

bool IsPi<T>(T value) where T : IFloatingPointIeee754<T>
=> value <= T.Pi && ((value >= T.Pi)); // Noncompliant

bool IsNotE<T>(T value) where T : IFloatingPointIeee754<T>
=> value > T.E || ((value < T.E)); // Noncompliant

bool AreEqual<T>(T first, T second) where T : IFloatingPointIeee754<T>
=> first == second; // Noncompliant

bool Equal<T>(T first, T second) where T : IBinaryFloatingPointIeee754<T>
=> first == second; // Noncompliant
}

public class ReportSpecificMessage_NaN
{
void HalfNaN(Half h)
{
Expand All @@ -22,26 +47,36 @@ public void M<T>(T t) where T : IFloatingPointIeee754<T>
if (t == T.NaN) { } // Noncompliant {{Do not check floating point equality with exact values, use 'T.IsNaN()' instead.}}
if (T.IsNaN(t)) { } // Compliant
}
}

bool HalfEqual(Half first, Half second)
=> first == second; // Noncompliant {{Do not check floating point equality with exact values, use a range instead.}}
// ^^

bool NFloatEqual(NFloat first, NFloat second)
=> first == second; // Noncompliant

bool IsEpsilon<T>(T value) where T : IFloatingPointIeee754<T>
=> value == T.Epsilon; // Noncompliant

bool IsPi<T>(T value) where T : IFloatingPointIeee754<T>
=> value <= T.Pi && ((value >= T.Pi)); // Noncompliant
public class ReportSpecificMessage_Infinities
{
void HalfInfinities(Half h)
{
_ = h == Half.PositiveInfinity; // Noncompliant {{Do not check floating point equality with exact values, use 'Half.IsPositiveInfinity()' instead.}}
_ = Half.NegativeInfinity == h; // Noncompliant {{Do not check floating point equality with exact values, use 'Half.IsNegativeInfinity()' instead.}}
}

bool IsNotE<T>(T value) where T : IFloatingPointIeee754<T>
=> value > T.E || ((value < T.E)); // Noncompliant
void NFloatInfinities(NFloat nf)
{
_ = nf == NFloat.PositiveInfinity; // Noncompliant {{Do not check floating point equality with exact values, use 'NFloat.IsPositiveInfinity()' instead.}}
_ = NFloat.NegativeInfinity == nf; // Noncompliant {{Do not check floating point equality with exact values, use 'NFloat.IsNegativeInfinity()' instead.}}
}
}

bool AreEqual<T>(T first, T second) where T : IFloatingPointIeee754<T>
=> first == second; // Noncompliant
namespace TestWithUsingStatic
{
using static System.Runtime.InteropServices.NFloat;

bool Equal<T>(T first, T second) where T : IBinaryFloatingPointIeee754<T>
=> first == second; // Noncompliant
public class ReportSpecificMessage
{
public void WithUsingStatic(double d)
{
_ = d == NaN; // Noncompliant {{Do not check floating point equality with exact values, use 'IsNaN()' instead.}}
_ = NaN == d; // Noncompliant {{Do not check floating point equality with exact values, use 'IsNaN()' instead.}}
_ = NaN == NaN; // Noncompliant {{Do not check floating point equality with exact values, use 'IsNaN()' instead.}}
_ = NaN == float.NaN; // Noncompliant {{Do not check floating point equality with exact values, use 'NFloat.IsNaN()' instead.}}
_ = double.NaN == NaN; // Noncompliant {{Do not check floating point equality with exact values, use 'double.IsNaN()' instead.}}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,37 @@ public void WithSingle()
}
}

public class ReportSpecificMessage_Infinities
{
public void WithDouble(double d)
{
_ = d == double.PositiveInfinity; // Noncompliant {{Do not check floating point equality with exact values, use 'double.IsPositiveInfinity()' instead.}}
_ = double.PositiveInfinity != d; // Noncompliant {{Do not check floating point inequality with exact values, use 'double.IsPositiveInfinity()' instead.}}
_ = d == double.NegativeInfinity; // Noncompliant {{Do not check floating point equality with exact values, use 'double.IsNegativeInfinity()' instead.}}
_ = double.NegativeInfinity != d; // Noncompliant {{Do not check floating point inequality with exact values, use 'double.IsNegativeInfinity()' instead.}}
}

public void WithFloat(float f)
{
_ = f == float.PositiveInfinity; // Noncompliant {{Do not check floating point equality with exact values, use 'float.IsPositiveInfinity()' instead.}}
_ = float.PositiveInfinity != f; // Noncompliant {{Do not check floating point inequality with exact values, use 'float.IsPositiveInfinity()' instead.}}
_ = f == float.NegativeInfinity; // Noncompliant {{Do not check floating point equality with exact values, use 'float.IsNegativeInfinity()' instead.}}
_ = float.NegativeInfinity != f; // Noncompliant {{Do not check floating point inequality with exact values, use 'float.IsNegativeInfinity()' instead.}}
}

public void WithDoublePascalCase(Double d)
{
_ = Double.PositiveInfinity == d; // Noncompliant {{Do not check floating point equality with exact values, use 'double.IsPositiveInfinity()' instead.}}
_ = d == System.Double.NegativeInfinity; // Noncompliant {{Do not check floating point equality with exact values, use 'double.IsNegativeInfinity()' instead.}}
}

public void WithSingle(Single f)
{
_ = Single.PositiveInfinity == f; // Noncompliant {{Do not check floating point equality with exact values, use 'float.IsPositiveInfinity()' instead.}}
_ = f == System.Single.NegativeInfinity; // Noncompliant {{Do not check floating point equality with exact values, use 'float.IsNegativeInfinity()' instead.}}
}
}

namespace TestsWithTypeAliases
{
using DoubleAlias = Double;
Expand Down

0 comments on commit b2817b2

Please sign in to comment.